gnome: add to contact

Adds context menu item which allows the user to choose an existing
contact or create a new contact to add the selected contact method
to.

Note: this currently seems to fail with non local address books
(specifically with google contacts), even though the EDS methods
will ocasionally return as having succeeded. This is a bug which
will be addressed in later patches.

Issue: #78234
Change-Id: Ia7dc4e8cf5cc5582d4f734f6e46d26cf4b2195dd
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 873dc24..d774107 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -229,12 +229,16 @@
    src/utils/files.h
    src/utils/files.cpp
    ${GIT_REVISION_OUTPUT_FILE}
-   src/createcontactdialog.h
-   src/createcontactdialog.cpp
    src/utils/menus.h
    src/utils/menus.cpp
    src/utils/accounts.h
    src/utils/accounts.cpp
+   src/contactpopover.h
+   src/contactpopover.cpp
+   src/choosecontactview.h
+   src/choosecontactview.cpp
+   src/editcontactview.h
+   src/editcontactview.cpp
 )
 
 # compile glib resource files to c code
diff --git a/src/backends/edscontactbackend.cpp b/src/backends/edscontactbackend.cpp
index 8ea145c..6f8f035 100644
--- a/src/backends/edscontactbackend.cpp
+++ b/src/backends/edscontactbackend.cpp
@@ -119,8 +119,7 @@
 
 bool EdsContactEditor::save(const Person* item)
 {
-    Q_UNUSED(item)
-    return false;
+    return collection_->savePerson(item);
 }
 
 bool EdsContactEditor::remove(const Person* item)
@@ -439,7 +438,8 @@
 {
     return (CollectionInterface::SupportedFeatures::NONE |
             CollectionInterface::SupportedFeatures::LOAD |
-            CollectionInterface::SupportedFeatures::ADD  );
+            CollectionInterface::SupportedFeatures::ADD  |
+            CollectionInterface::SupportedFeatures::SAVE );
 }
 
 bool EdsContactBackend::clear()
@@ -456,12 +456,14 @@
 
 bool EdsContactBackend::addNewPerson(Person *item)
 {
-    if (!client_) return false;
+    g_return_val_if_fail(client_.get(), false);
 
     auto contact = e_contact_new_from_vcard(item->toVCard().constData());
     gchar *uid = NULL;
     GError *error = NULL;
 
+    /* FIXME: this methods returns True for a google addressbook, but it never
+     * actually adds the new contact... not clear if this is possible */
     bool ret = e_book_client_add_contact_sync(
         E_BOOK_CLIENT(client_.get()),
         contact,
@@ -486,3 +488,36 @@
 
     return ret;
 }
+
+bool EdsContactBackend::savePerson(const Person *item)
+{
+    g_return_val_if_fail(client_.get(), false);
+
+    g_debug("saving person");
+
+    auto contact = e_contact_new_from_vcard(item->toVCard().constData());
+    GError *error = NULL;
+
+    /* FIXME: this methods fails for a google addressbook, not clear if it is
+     * possible to edit a google address book... gnome contacts simply creates
+     * a local contact with the same uid */
+    bool ret = e_book_client_modify_contact_sync(
+        E_BOOK_CLIENT(client_.get()),
+        contact,
+        cancellable_.get(),
+        &error
+    );
+
+    if (!ret) {
+        if (error) {
+            g_warning("could not modify contact: %s", error->message);
+            g_clear_error(&error);
+        } else {
+            g_warning("could not modify contact");
+        }
+    }
+
+    g_object_unref(contact);
+
+    return ret;
+}
diff --git a/src/backends/edscontactbackend.h b/src/backends/edscontactbackend.h
index 2b27dc5..f53e626 100644
--- a/src/backends/edscontactbackend.h
+++ b/src/backends/edscontactbackend.h
@@ -91,6 +91,7 @@
     void parseContact(EContact *contact);
     void lastContactAdded();
     bool addNewPerson(Person *item);
+    bool savePerson(const Person *item);
 
 private:
    CollectionMediator<Person>*  mediator_;
diff --git a/src/callsview.cpp b/src/callsview.cpp
index 6740499..581a6f3 100644
--- a/src/callsview.cpp
+++ b/src/callsview.cpp
@@ -167,7 +167,16 @@
         if (auto call = var_c.value<Call *>()) {
             auto contactmethod = call->peerContactMethod();
             if (!contact_method_has_contact(contactmethod)) {
-                auto add_to = menu_item_contact_add_to(contactmethod, GTK_WIDGET(treeview));
+                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);
 
                 /* no other items, so show menu here */
diff --git a/src/choosecontactview.cpp b/src/choosecontactview.cpp
new file mode 100644
index 0000000..1f8f6c9
--- /dev/null
+++ b/src/choosecontactview.cpp
@@ -0,0 +1,244 @@
+/*
+ *  Copyright (C) 2015 Savoir-faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#include "choosecontactview.h"
+
+#include <contactmethod.h>
+#include <personmodel.h>
+#include <QtCore/QSortFilterProxyModel>
+#include <memory>
+#include "models/gtkqsortfiltertreemodel.h"
+#include "delegates/pixbufdelegate.h"
+#include "utils/models.h"
+
+enum
+{
+    PERSON_SELECTED,
+    NEW_PERSON_CLICKED,
+
+    LAST_SIGNAL
+};
+
+struct _ChooseContactView
+{
+    GtkBox parent;
+};
+
+struct _ChooseContactViewClass
+{
+    GtkBoxClass parent_class;
+};
+
+typedef struct _ChooseContactViewPrivate ChooseContactViewPrivate;
+
+struct _ChooseContactViewPrivate
+{
+    GtkWidget *treeview_choose_contact;
+    GtkWidget *button_create_contact;
+
+    ContactMethod *cm;
+
+    QSortFilterProxyModel *sorted_contacts;
+};
+
+static guint choose_contact_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_PRIVATE(ChooseContactView, choose_contact_view, GTK_TYPE_BOX);
+
+#define CHOOSE_CONTACT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHOOSE_CONTACT_VIEW_TYPE, ChooseContactViewPrivate))
+
+static void
+render_contact_photo(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
+                     GtkCellRenderer *cell,
+                     GtkTreeModel *tree_model,
+                     GtkTreeIter *iter,
+                     G_GNUC_UNUSED gpointer data)
+{
+    /* show a photo for the top level (Person) */
+    GtkTreePath *path = gtk_tree_model_get_path(tree_model, iter);
+    int depth = gtk_tree_path_get_depth(path);
+    gtk_tree_path_free(path);
+    if (depth == 1) {
+        /* get person */
+        QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(tree_model), iter);
+        if (idx.isValid()) {
+            QVariant var_c = idx.data(static_cast<int>(Person::Role::Object));
+            Person *c = var_c.value<Person *>();
+            /* get photo */
+            QVariant var_p = PixbufDelegate::instance()->contactPhoto(c, QSize(50, 50), false);
+            std::shared_ptr<GdkPixbuf> photo = var_p.value<std::shared_ptr<GdkPixbuf>>();
+            g_object_set(G_OBJECT(cell), "pixbuf", photo.get(), NULL);
+            return;
+        }
+    }
+
+    /* otherwise, make sure its an empty pixbuf */
+    g_object_set(G_OBJECT(cell), "pixbuf", NULL, NULL);
+}
+
+static void
+select_cb(ChooseContactView *self)
+{
+    g_return_if_fail(IS_CHOOSE_CONTACT_VIEW(self));
+    ChooseContactViewPrivate *priv = CHOOSE_CONTACT_VIEW_GET_PRIVATE(self);
+
+    /* get the selected collection */
+    auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_choose_contact));
+    auto idx = get_index_from_selection(selection);
+    if (idx.isValid()) {
+        auto p = idx.data(static_cast<int>(Person::Role::Object)).value<Person *>();
+
+        g_signal_emit(self, choose_contact_signals[PERSON_SELECTED], 0, p);
+    } else {
+        g_warning("invalid Person selected");
+    }
+}
+
+static void
+create_contact_cb(G_GNUC_UNUSED GtkButton *button, ChooseContactView *self)
+{
+    g_return_if_fail(IS_CHOOSE_CONTACT_VIEW(self));
+
+    g_signal_emit(self, choose_contact_signals[NEW_PERSON_CLICKED], 0);
+}
+
+static void
+choose_contact_view_init(ChooseContactView *self)
+{
+    gtk_widget_init_template(GTK_WIDGET(self));
+
+    ChooseContactViewPrivate *priv = CHOOSE_CONTACT_VIEW_GET_PRIVATE(self);
+
+    priv->sorted_contacts = new QSortFilterProxyModel(PersonModel::instance());
+    priv->sorted_contacts->setSourceModel(PersonModel::instance());
+    priv->sorted_contacts->setSortCaseSensitivity(Qt::CaseInsensitive);
+    priv->sorted_contacts->sort(0);
+
+    auto contacts_model = gtk_q_sort_filter_tree_model_new(
+        priv->sorted_contacts,
+        1,
+        Qt::DisplayRole, G_TYPE_STRING);
+    gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_choose_contact), GTK_TREE_MODEL(contacts_model));
+    g_object_unref(contacts_model); /* the model should be freed when the view is destroyed */
+
+    /* photo and name/contact method colparentumn */
+    GtkCellArea *area = gtk_cell_area_box_new();
+    GtkTreeViewColumn *column = gtk_tree_view_column_new_with_area(area);
+    gtk_tree_view_column_set_title(column, "Name");
+
+    /* photo renderer */
+    GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
+    gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
+
+    /* get the photo */
+    gtk_tree_view_column_set_cell_data_func(
+        column,
+        renderer,
+        (GtkTreeCellDataFunc)render_contact_photo,
+        NULL,
+        NULL);
+
+    /* name and contact method renderer */
+    renderer = gtk_cell_renderer_text_new();
+    g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+    gtk_cell_area_box_pack_start(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
+    gtk_tree_view_column_add_attribute(column, renderer, "text", 0);
+
+    gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_choose_contact), column);
+    gtk_tree_view_column_set_resizable(column, TRUE);
+
+    /* connect to the button signals */
+    g_signal_connect_swapped(priv->treeview_choose_contact, "row-activated", G_CALLBACK(select_cb), self);
+    g_signal_connect(priv->button_create_contact, "clicked", G_CALLBACK(create_contact_cb), self);
+}
+
+static void
+choose_contact_view_dispose(GObject *object)
+{
+    G_OBJECT_CLASS(choose_contact_view_parent_class)->dispose(object);
+}
+
+static void
+choose_contact_view_finalize(GObject *object)
+{
+    ChooseContactView *self = CHOOSE_CONTACT_VIEW(object);
+    ChooseContactViewPrivate *priv = CHOOSE_CONTACT_VIEW_GET_PRIVATE(self);
+
+    delete priv->sorted_contacts;
+
+    G_OBJECT_CLASS(choose_contact_view_parent_class)->finalize(object);
+}
+
+static void
+choose_contact_view_class_init(ChooseContactViewClass *klass)
+{
+    G_OBJECT_CLASS(klass)->finalize = choose_contact_view_finalize;
+    G_OBJECT_CLASS(klass)->dispose = choose_contact_view_dispose;
+
+    choose_contact_signals[NEW_PERSON_CLICKED] =
+        g_signal_new("new-person-clicked",
+            G_OBJECT_CLASS_TYPE(G_OBJECT_CLASS(klass)),
+            (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+            0, /* class offset */
+            NULL, /* accumulater */
+            NULL, /* accu data */
+            g_cclosure_marshal_VOID__VOID,
+            G_TYPE_NONE, 0);
+
+    choose_contact_signals[PERSON_SELECTED] =
+        g_signal_new ("person-selected",
+            G_OBJECT_CLASS_TYPE(G_OBJECT_CLASS(klass)),
+            (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+            0, /* class offset */
+            NULL, /* accumulater */
+            NULL, /* accu data */
+            g_cclosure_marshal_VOID__POINTER,
+            G_TYPE_NONE,
+            1, G_TYPE_POINTER);
+
+    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(klass),
+                                                "/cx/ring/RingGnome/choosecontactview.ui");
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), ChooseContactView, treeview_choose_contact);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), ChooseContactView, button_create_contact);
+}
+
+GtkWidget *
+choose_contact_view_new(ContactMethod *cm)
+{
+    g_return_val_if_fail(cm, NULL);
+
+    gpointer self = g_object_new(CHOOSE_CONTACT_VIEW_TYPE, NULL);
+
+    ChooseContactViewPrivate *priv = CHOOSE_CONTACT_VIEW_GET_PRIVATE(self);
+    priv->cm = cm;
+
+    return (GtkWidget *)self;
+}
diff --git a/src/createcontactdialog.h b/src/choosecontactview.h
similarity index 61%
rename from src/createcontactdialog.h
rename to src/choosecontactview.h
index 541f1eb..20ce78f 100644
--- a/src/createcontactdialog.h
+++ b/src/choosecontactview.h
@@ -28,8 +28,8 @@
  *  as that of the covered work.
  */
 
-#ifndef _CREATECONTACTDIALOG_H
-#define _CREATECONTACTDIALOG_H
+#ifndef _CHOOSECONTACTVIEW_H
+#define _CHOOSECONTACTVIEW_H
 
 #include <gtk/gtk.h>
 
@@ -37,18 +37,18 @@
 
 class ContactMethod;
 
-#define CREATE_CONTACT_DIALOG_TYPE            (create_contact_dialog_get_type ())
-#define CREATE_CONTACT_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CREATE_CONTACT_DIALOG_TYPE, CreateContactDialog))
-#define CREATE_CONTACT_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), CREATE_CONTACT_DIALOG_TYPE, CreateContactDialogClass))
-#define IS_CREATE_CONTACT_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), CREATE_CONTACT_DIALOG_TYPE))
-#define IS_CREATE_CONTACT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CREATE_CONTACT_DIALOG_TYPE))
+#define CHOOSE_CONTACT_VIEW_TYPE            (choose_contact_view_get_type ())
+#define CHOOSE_CONTACT_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CHOOSE_CONTACT_VIEW_TYPE, ChooseContactView))
+#define CHOOSE_CONTACT_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), CHOOSE_CONTACT_VIEW_TYPE, ChooseContactViewClass))
+#define IS_CHOOSE_CONTACT_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), CHOOSE_CONTACT_VIEW_TYPE))
+#define IS_CHOOSE_CONTACT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CHOOSE_CONTACT_VIEW_TYPE))
 
-typedef struct _CreateContactDialog      CreateContactDialog;
-typedef struct _CreateContactDialogClass CreateContactDialogClass;
+typedef struct _ChooseContactView      ChooseContactView;
+typedef struct _ChooseContactViewClass ChooseContactViewClass;
 
-GType      create_contact_dialog_get_type  (void) G_GNUC_CONST;
-GtkWidget *create_contact_dialog_new       (ContactMethod *cm, GtkWidget *parent);
+GType      choose_contact_view_get_type  (void) G_GNUC_CONST;
+GtkWidget *choose_contact_view_new       (ContactMethod *cm);
 
 G_END_DECLS
 
-#endif /* _CREATECONTACTDIALOG_H */
+#endif /* _CHOOSECONTACTVIEW_H */
diff --git a/src/contactpopover.cpp b/src/contactpopover.cpp
new file mode 100644
index 0000000..b41038c
--- /dev/null
+++ b/src/contactpopover.cpp
@@ -0,0 +1,229 @@
+/*
+ *  Copyright (C) 2015 Savoir-faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#include "contactpopover.h"
+
+#include <contactmethod.h>
+#include "choosecontactview.h"
+#include "editcontactview.h"
+
+struct _ContactPopover
+{
+#if GTK_CHECK_VERSION(3,12,0)
+    GtkPopover parent;
+#else
+    GtkWindow parent;
+#endif
+};
+
+struct _ContactPopoverClass
+{
+#if GTK_CHECK_VERSION(3,12,0)
+    GtkPopoverClass parent_class;
+#else
+    GtkWindowClass parent_class;
+#endif
+};
+
+typedef struct _ContactPopoverPrivate ContactPopoverPrivate;
+
+struct _ContactPopoverPrivate
+{
+    GtkWidget *choosecontactview;
+    GtkWidget *editcontactview;
+
+    ContactMethod *cm;
+};
+
+#if GTK_CHECK_VERSION(3,12,0)
+    G_DEFINE_TYPE_WITH_PRIVATE(ContactPopover, contact_popover, GTK_TYPE_POPOVER);
+#else
+    G_DEFINE_TYPE_WITH_PRIVATE(ContactPopover, contact_popover, GTK_TYPE_WINDOW);
+#endif
+
+
+#define CONTACT_POPOVER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CONTACT_POPOVER_TYPE, ContactPopoverPrivate))
+
+#if !GTK_CHECK_VERSION(3,12,0)
+static gboolean
+contact_popover_button_release(GtkWidget *self, GdkEventButton *event)
+{
+    auto child = gtk_bin_get_child(GTK_BIN(self));
+
+    auto event_widget = gtk_get_event_widget((GdkEvent *) event);
+
+    GtkAllocation child_alloc;
+
+    gtk_widget_get_allocation(child, &child_alloc);
+
+    if (event->x < child_alloc.x ||
+        event->x > child_alloc.x + child_alloc.width ||
+        event->y < child_alloc.y ||
+        event->y > child_alloc.y + child_alloc.height)
+        gtk_widget_destroy(self);
+    else if (!gtk_widget_is_ancestor(event_widget, self))
+        gtk_widget_destroy(self);
+
+    return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+contact_popover_key_press(GtkWidget *self, GdkEventKey *event)
+{
+    if (event->keyval == GDK_KEY_Escape) {
+        gtk_widget_destroy(self);
+        return GDK_EVENT_STOP;
+    }
+
+    return GDK_EVENT_PROPAGATE;
+}
+#endif
+
+static void
+contact_popover_init(ContactPopover *self)
+{
+#if GTK_CHECK_VERSION(3,12,0)
+    /* for now, destroy the popover on close, as we will construct a new one
+     * each time we need it */
+    g_signal_connect(self, "closed", G_CALLBACK(gtk_widget_destroy), NULL);
+#else
+    /* destroy the window on ESC, or when the user clicks outside of it */
+    g_signal_connect(self, "button_release_event", G_CALLBACK(contact_popover_button_release), NULL);
+    g_signal_connect(self, "key_press_event", G_CALLBACK(contact_popover_key_press), NULL);
+#endif
+}
+
+static void
+contact_popover_dispose(GObject *object)
+{
+    G_OBJECT_CLASS(contact_popover_parent_class)->dispose(object);
+}
+
+static void
+contact_popover_finalize(GObject *object)
+{
+    G_OBJECT_CLASS(contact_popover_parent_class)->finalize(object);
+}
+static void
+contact_popover_class_init(ContactPopoverClass *klass)
+{
+    G_OBJECT_CLASS(klass)->finalize = contact_popover_finalize;
+    G_OBJECT_CLASS(klass)->dispose = contact_popover_dispose;
+}
+
+static void
+construct_edit_contact_view(ContactPopover *self, Person *p)
+{
+    g_return_if_fail(IS_CONTACT_POPOVER(self));
+    ContactPopoverPrivate *priv = CONTACT_POPOVER_GET_PRIVATE(self);
+
+    priv->editcontactview = edit_contact_view_new(priv->cm, p);
+    g_object_add_weak_pointer(G_OBJECT(priv->editcontactview), (gpointer *)&priv->editcontactview);
+
+    gtk_container_remove(GTK_CONTAINER(self), priv->choosecontactview);
+    gtk_container_add(GTK_CONTAINER(self), priv->editcontactview);
+
+#if !GTK_CHECK_VERSION(3,12,0)
+    /* resize the window to shrink to the new view */
+    gtk_window_resize(GTK_WINDOW(self), 1, 1);
+#endif
+
+    /* destroy this popover when the contact is saved */
+    g_signal_connect_swapped(priv->editcontactview, "person-saved", G_CALLBACK(gtk_widget_destroy), self);
+}
+
+static void
+new_person_clicked(ContactPopover *self)
+{
+    g_return_if_fail(IS_CONTACT_POPOVER(self));
+    construct_edit_contact_view(self, NULL);
+}
+
+static void
+person_selected(ContactPopover *self, Person *p)
+{
+    g_return_if_fail(IS_CONTACT_POPOVER(self));
+    construct_edit_contact_view(self, p);
+}
+
+/**
+ * For gtk+ >= 3.12 this will create a GtkPopover pointing to the parent and if
+ * given, the GdkRectangle. Otherwise, this will create an undecorated GtkWindow
+ * which will be centered on the toplevel window of the given parent.
+ * This is to ensure cmpatibility with gtk+3.10.
+ */
+GtkWidget *
+contact_popover_new(ContactMethod *cm, GtkWidget *parent,
+#if !GTK_CHECK_VERSION(3,12,0)
+                    G_GNUC_UNUSED
+#endif
+                    GdkRectangle *rect)
+{
+    g_return_val_if_fail(cm, NULL);
+
+#if GTK_CHECK_VERSION(3,12,0)
+    gpointer self = g_object_new(CONTACT_POPOVER_TYPE,
+                                 "relative-to", parent,
+                                 "position", GTK_POS_RIGHT,
+                                 NULL);
+
+    if (rect)
+        gtk_popover_set_pointing_to(GTK_POPOVER(self), rect);
+#else
+    /* get the toplevel parent and try to center on it */
+    if (parent && GTK_IS_WIDGET(parent)) {
+        parent = gtk_widget_get_toplevel(GTK_WIDGET(parent));
+        if (!gtk_widget_is_toplevel(parent)) {
+            parent = NULL;
+            g_debug("could not get top level parent");
+        }
+    }
+
+    gpointer self = g_object_new(CONTACT_POPOVER_TYPE,
+                                 "modal", TRUE,
+                                 "transient-for", parent,
+                                 "window-position", GTK_WIN_POS_CENTER_ON_PARENT,
+                                 "decorated", FALSE,
+                                 "resizable", FALSE,
+                                 NULL);
+#endif
+
+    ContactPopoverPrivate *priv = CONTACT_POPOVER_GET_PRIVATE(self);
+    priv->cm = cm;
+
+    priv->choosecontactview = choose_contact_view_new(cm);
+    gtk_container_add(GTK_CONTAINER(self), priv->choosecontactview);
+    g_object_add_weak_pointer(G_OBJECT(priv->choosecontactview), (gpointer *)&priv->choosecontactview);
+
+    g_signal_connect_swapped(priv->choosecontactview, "new-person-clicked", G_CALLBACK(new_person_clicked), self);
+    g_signal_connect_swapped(priv->choosecontactview, "person-selected", G_CALLBACK(person_selected), self);
+
+    return (GtkWidget *)self;
+}
diff --git a/src/contactpopover.h b/src/contactpopover.h
new file mode 100644
index 0000000..6b3b801
--- /dev/null
+++ b/src/contactpopover.h
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (C) 2015 Savoir-faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#ifndef _CONTACTPOPOVER_H
+#define _CONTACTPOPOVER_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+class ContactMethod;
+
+#define CONTACT_POPOVER_TYPE            (contact_popover_get_type ())
+#define CONTACT_POPOVER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CONTACT_POPOVER_TYPE, ContactPopover))
+#define CONTACT_POPOVER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), CONTACT_POPOVER_TYPE, ContactPopoverClass))
+#define IS_CONTACT_POPOVER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), CONTACT_POPOVER_TYPE))
+#define IS_CONTACT_POPOVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CONTACT_POPOVER_TYPE))
+
+typedef struct _ContactPopover      ContactPopover;
+typedef struct _ContactPopoverClass ContactPopoverClass;
+
+GType      contact_popover_get_type  (void) G_GNUC_CONST;
+
+/**
+ * For gtk+ >= 3.12 this will create a GtkPopover pointing to the parent and if
+ * given, the GdkRectangle. Otherwise, this will create an undecorated GtkWindow
+ * which will be centered on the toplevel window of the given parent.
+ * This is to ensure cmpatibility with gtk+3.10.
+ */
+GtkWidget *contact_popover_new       (ContactMethod *cm, GtkWidget *parent, GdkRectangle *rect);
+
+G_END_DECLS
+
+#endif /* _CONTACTPOPOVER_H */
diff --git a/src/createcontactdialog.cpp b/src/createcontactdialog.cpp
deleted file mode 100644
index 5d77255..0000000
--- a/src/createcontactdialog.cpp
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- *  Copyright (C) 2015 Savoir-faire Linux Inc.
- *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
- *
- *  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 3 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, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- *
- *  Additional permission under GNU GPL version 3 section 7:
- *
- *  If you modify this program, or any covered work, by linking or
- *  combining it with the OpenSSL project's OpenSSL library (or a
- *  modified version of that library), containing parts covered by the
- *  terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
- *  grants you additional permission to convey the resulting work.
- *  Corresponding Source for a non-source form of such a combination
- *  shall include the source code for the parts of OpenSSL used as well
- *  as that of the covered work.
- */
-
-#include "createcontactdialog.h"
-
-#include <contactmethod.h>
-#include <personmodel.h>
-#include <numbercategorymodel.h>
-#include "utils/models.h"
-
-struct _CreateContactDialog
-{
-    GtkDialog parent;
-};
-
-struct _CreateContactDialogClass
-{
-    GtkDialogClass parent_class;
-};
-
-typedef struct _CreateContactDialogPrivate CreateContactDialogPrivate;
-
-struct _CreateContactDialogPrivate
-{
-    GtkWidget *button_save;
-    GtkWidget *button_cancel;
-    GtkWidget *combobox_addressbook;
-    GtkWidget *entry_name;
-    GtkWidget *combobox_detail;
-    GtkWidget *entry_contactmethod;
-
-    ContactMethod *cm;
-};
-
-G_DEFINE_TYPE_WITH_PRIVATE(CreateContactDialog, create_contact_dialog, GTK_TYPE_DIALOG);
-
-#define CREATE_CONTACT_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CREATE_CONTACT_DIALOG_TYPE, CreateContactDialogPrivate))
-
-static void
-save_cb(G_GNUC_UNUSED GtkButton *button, CreateContactDialog *self)
-{
-    g_return_if_fail(IS_CREATE_CONTACT_DIALOG(self));
-    CreateContactDialogPrivate *priv = CREATE_CONTACT_DIALOG_GET_PRIVATE(self);
-
-    /* make sure that the entry is not empty */
-    auto name = gtk_entry_get_text(GTK_ENTRY(priv->entry_name));
-    if (!name or strlen(name) == 0) {
-        g_warning("new contact must have a name");
-        gtk_widget_grab_focus(priv->entry_name);
-        return;
-    }
-
-    /* get the selected collection */
-    GtkTreeIter iter;
-    gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->combobox_addressbook), &iter);
-    auto addressbook_model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->combobox_addressbook));
-    GValue value = G_VALUE_INIT;
-    gtk_tree_model_get_value(addressbook_model, &iter, 1, &value);
-    auto collection = (CollectionInterface *)g_value_get_pointer(&value);
-
-    /* get the selected number category */
-    const auto& idx = gtk_combo_box_get_index(GTK_COMBO_BOX(priv->combobox_detail));
-    if (idx.isValid()) {
-        auto category = NumberCategoryModel::instance()->getCategory(idx.data().toString());
-        priv->cm->setCategory(category);
-    }
-
-    /* create a new person */
-    Person *p = new Person(collection);
-    p->setFormattedName(name);
-
-    /* associate the new person with the contact method */
-    Person::ContactMethods numbers;
-    numbers << priv->cm;
-    p->setContactMethods(numbers);
-
-    PersonModel::instance()->addNewPerson(p, collection);
-
-    gtk_dialog_response(GTK_DIALOG(self), GTK_RESPONSE_OK);
-}
-
-static void
-cancel_cb(G_GNUC_UNUSED GtkButton *button, CreateContactDialog *self)
-{
-    gtk_dialog_response(GTK_DIALOG(self), GTK_RESPONSE_CANCEL);
-}
-
-static void
-create_contact_dialog_init(CreateContactDialog *self)
-{
-    gtk_widget_init_template(GTK_WIDGET(self));
-
-    CreateContactDialogPrivate *priv = CREATE_CONTACT_DIALOG_GET_PRIVATE(self);
-
-    /* model for the combobox for the choice of addressbooks */
-    auto addressbook_model = gtk_list_store_new(
-        2, G_TYPE_STRING, G_TYPE_POINTER
-    );
-
-    gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_addressbook),
-                            GTK_TREE_MODEL(addressbook_model));
-    GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
-    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_addressbook),
-                               renderer, FALSE);
-    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(priv->combobox_addressbook),
-                                   renderer, "text", 0, NULL);
-
-    /* get all the available contact backends (addressbooks) */
-    const auto& collections = PersonModel::instance()->enabledCollections();
-    for (int i = 0; i < collections.size(); ++i) {
-        GtkTreeIter iter;
-        gtk_list_store_append(addressbook_model, &iter);
-        gtk_list_store_set(addressbook_model, &iter,
-                           0, collections.at(i)->name().toUtf8().constData(),
-                           1, collections.at(i),
-                           -1);
-    }
-
-    /* the first addressbook loaded is probably the vCard... so select the next
-     * one to be used by default */
-    gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_addressbook), 1);
-
-    /* model for the available details to choose from */
-    gtk_combo_box_set_qmodel(GTK_COMBO_BOX(priv->combobox_detail),
-                             (QAbstractItemModel *)NumberCategoryModel::instance(), NULL);
-
-    /* set "home" as the default number category */
-    const auto& idx = NumberCategoryModel::instance()->nameToIndex("home");
-    if (idx.isValid())
-        gtk_combo_box_set_active_index(GTK_COMBO_BOX(priv->combobox_detail), idx);
-
-    /* set a default name in the name entry, and grab focus to select the text */
-    gtk_entry_set_text(GTK_ENTRY(priv->entry_name), "New Contact");
-    gtk_widget_grab_focus(GTK_WIDGET(priv->entry_name));
-
-    /* connect to the button signals */
-    g_signal_connect(priv->button_save, "clicked", G_CALLBACK(save_cb), self);
-    g_signal_connect(priv->button_cancel, "clicked", G_CALLBACK(cancel_cb), self);
-}
-
-static void
-create_contact_dialog_dispose(GObject *object)
-{
-    G_OBJECT_CLASS(create_contact_dialog_parent_class)->dispose(object);
-}
-
-static void
-create_contact_dialog_finalize(GObject *object)
-{
-    G_OBJECT_CLASS(create_contact_dialog_parent_class)->finalize(object);
-}
-
-static void
-create_contact_dialog_class_init(CreateContactDialogClass *klass)
-{
-    G_OBJECT_CLASS(klass)->finalize = create_contact_dialog_finalize;
-    G_OBJECT_CLASS(klass)->dispose = create_contact_dialog_dispose;
-
-    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(klass),
-                                                "/cx/ring/RingGnome/createcontactdialog.ui");
-
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), CreateContactDialog, button_save);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), CreateContactDialog, button_cancel);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), CreateContactDialog, combobox_addressbook);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), CreateContactDialog, entry_name);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), CreateContactDialog, combobox_detail);
-    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), CreateContactDialog, entry_contactmethod);
-}
-
-GtkWidget *
-create_contact_dialog_new(ContactMethod *cm, GtkWidget *parent)
-{
-    g_return_val_if_fail(cm, NULL);
-
-    if (parent && GTK_IS_WIDGET(parent)) {
-        /* get parent window so we can center on it */
-        parent = gtk_widget_get_toplevel(GTK_WIDGET(parent));
-        if (!gtk_widget_is_toplevel(parent)) {
-            parent = NULL;
-            g_debug("could not get top level parent");
-        }
-    }
-
-    gpointer self = g_object_new(CREATE_CONTACT_DIALOG_TYPE, "transient-for", parent, NULL);
-
-    CreateContactDialogPrivate *priv = CREATE_CONTACT_DIALOG_GET_PRIVATE(self);
-
-    priv->cm = cm;
-    gtk_entry_set_text(GTK_ENTRY(priv->entry_contactmethod), cm->uri().toUtf8().constData());
-    gtk_entry_set_width_chars(GTK_ENTRY(priv->entry_contactmethod), cm->uri().toUtf8().size());
-
-    return (GtkWidget *)self;
-}
diff --git a/src/editcontactview.cpp b/src/editcontactview.cpp
new file mode 100644
index 0000000..ceef583
--- /dev/null
+++ b/src/editcontactview.cpp
@@ -0,0 +1,293 @@
+/*
+ *  Copyright (C) 2015 Savoir-faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#include "editcontactview.h"
+
+#include <contactmethod.h>
+#include <personmodel.h>
+#include <numbercategorymodel.h>
+#include "utils/models.h"
+
+enum
+{
+    PERSON_SAVED,
+
+    LAST_SIGNAL
+};
+
+struct _EditContactView
+{
+    GtkGrid parent;
+};
+
+struct _EditContactViewClass
+{
+    GtkGridClass parent_class;
+};
+
+typedef struct _EditContactViewPrivate EditContactViewPrivate;
+
+struct _EditContactViewPrivate
+{
+    GtkWidget *button_save;
+    GtkWidget *combobox_addressbook;
+    GtkWidget *entry_name;
+    GtkWidget *combobox_detail;
+    GtkWidget *label_uri;
+
+    ContactMethod *cm;
+    Person        *person;
+};
+
+static guint edit_contact_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE_WITH_PRIVATE(EditContactView, edit_contact_view, GTK_TYPE_GRID);
+
+#define EDIT_CONTACT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EDIT_CONTACT_VIEW_TYPE, EditContactViewPrivate))
+
+static void
+save_cb(EditContactView *self)
+{
+    g_return_if_fail(IS_EDIT_CONTACT_VIEW(self));
+    EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
+
+    auto name = gtk_entry_get_text(GTK_ENTRY(priv->entry_name));
+    if (!priv->person) {
+        /* make sure that the entry is not empty */
+        if (!name or strlen(name) == 0) {
+            g_warning("new contact must have a name");
+            gtk_widget_grab_focus(priv->entry_name);
+            return;
+        }
+    }
+
+    /* get the selected number category */
+    const auto& idx = gtk_combo_box_get_index(GTK_COMBO_BOX(priv->combobox_detail));
+    if (idx.isValid()) {
+        auto category = NumberCategoryModel::instance()->getCategory(idx.data().toString());
+        priv->cm->setCategory(category);
+    }
+
+    if (!priv->person) {
+        /* get the selected collection */
+        GtkTreeIter iter;
+        gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->combobox_addressbook), &iter);
+        auto addressbook_model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->combobox_addressbook));
+        GValue value = G_VALUE_INIT;
+        gtk_tree_model_get_value(addressbook_model, &iter, 1, &value);
+        auto collection = (CollectionInterface *)g_value_get_pointer(&value);
+
+        /* create a new person */
+        priv->person = new Person(collection);
+        priv->person->setFormattedName(name);
+
+        /* associate the new person with the contact method */
+        Person::ContactMethods numbers;
+        numbers << priv->cm;
+        priv->person->setContactMethods(numbers);
+
+        PersonModel::instance()->addNewPerson(priv->person, collection);
+    } else {
+        auto numbers = priv->person->phoneNumbers();
+        numbers << priv->cm;
+        priv->person->setContactMethods(numbers);
+        priv->person->save();
+    }
+
+    g_signal_emit(self, edit_contact_signals[PERSON_SAVED], 0);
+}
+
+static void
+name_changed(GtkEntry *entry_name, EditContactView *self)
+{
+    g_return_if_fail(IS_EDIT_CONTACT_VIEW(self));
+    EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
+
+    auto name = gtk_entry_get_text(entry_name);
+    /* make sure that the entry is not empty */
+    if (!name or strlen(name) == 0) {
+        gtk_widget_set_sensitive(priv->button_save, FALSE);
+    } else {
+        gtk_widget_set_sensitive(priv->button_save, TRUE);
+    }
+}
+
+static void
+edit_contact_view_init(EditContactView *self)
+{
+    gtk_widget_init_template(GTK_WIDGET(self));
+
+    EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
+
+    /* model for the combobox for the choice of addressbooks */
+    auto addressbook_model = gtk_list_store_new(
+        2, G_TYPE_STRING, G_TYPE_POINTER
+    );
+
+    gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_addressbook),
+                            GTK_TREE_MODEL(addressbook_model));
+    GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
+    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_addressbook),
+                               renderer, FALSE);
+    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(priv->combobox_addressbook),
+                                   renderer, "text", 0, NULL);
+
+    /* get all the available contact backends (addressbooks) */
+    const auto& collections = PersonModel::instance()->enabledCollections();
+    for (int i = 0; i < collections.size(); ++i) {
+        GtkTreeIter iter;
+        gtk_list_store_append(addressbook_model, &iter);
+        gtk_list_store_set(addressbook_model, &iter,
+                           0, collections.at(i)->name().toUtf8().constData(),
+                           1, collections.at(i),
+                           -1);
+    }
+
+    /* set the first addressbook which is not the "vCard" to be used, unless its
+     * the only one */
+    GtkTreeIter iter_to_select;
+    if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(addressbook_model), &iter_to_select)) {
+        GValue value = G_VALUE_INIT;
+        gtk_tree_model_get_value(GTK_TREE_MODEL(addressbook_model), &iter_to_select, 1, &value);
+        auto collection = (CollectionInterface *)g_value_get_pointer(&value);
+        GtkTreeIter iter = iter_to_select;
+        while ( (collection->name() == "vCard") && gtk_tree_model_iter_next(GTK_TREE_MODEL(addressbook_model), &iter)) {
+            iter_to_select = iter;
+            value = G_VALUE_INIT;
+            gtk_tree_model_get_value(GTK_TREE_MODEL(addressbook_model), &iter, 1, &value);
+            collection = (CollectionInterface *)g_value_get_pointer(&value);
+        }
+    }
+    gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combobox_addressbook), &iter_to_select);
+
+    /* model for the available details to choose from */
+    gtk_combo_box_set_qmodel(GTK_COMBO_BOX(priv->combobox_detail),
+                             (QAbstractItemModel *)NumberCategoryModel::instance(), NULL);
+
+    /* set "home" as the default number category */
+    const auto& idx = NumberCategoryModel::instance()->nameToIndex("home");
+    if (idx.isValid())
+        gtk_combo_box_set_active_index(GTK_COMBO_BOX(priv->combobox_detail), idx);
+
+    /* disable save button until name is entered */
+    gtk_widget_set_sensitive(priv->button_save, FALSE);
+
+    /* connect to the button signals */
+    g_signal_connect_swapped(priv->button_save, "clicked", G_CALLBACK(save_cb), self);
+    g_signal_connect(priv->entry_name, "changed", G_CALLBACK(name_changed), self);
+}
+
+static void
+edit_contact_view_dispose(GObject *object)
+{
+    G_OBJECT_CLASS(edit_contact_view_parent_class)->dispose(object);
+}
+
+static void
+edit_contact_view_finalize(GObject *object)
+{
+    G_OBJECT_CLASS(edit_contact_view_parent_class)->finalize(object);
+}
+
+static void
+edit_contact_view_class_init(EditContactViewClass *klass)
+{
+    G_OBJECT_CLASS(klass)->finalize = edit_contact_view_finalize;
+    G_OBJECT_CLASS(klass)->dispose = edit_contact_view_dispose;
+
+    edit_contact_signals[PERSON_SAVED] =
+        g_signal_new("person-saved",
+            G_OBJECT_CLASS_TYPE(G_OBJECT_CLASS(klass)),
+            (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+            0, /* class offset */
+            NULL, /* accumulater */
+            NULL, /* accu data */
+            g_cclosure_marshal_VOID__VOID,
+            G_TYPE_NONE, 0);
+
+    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(klass),
+                                                "/cx/ring/RingGnome/editcontactview.ui");
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, button_save);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, combobox_addressbook);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, entry_name);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, combobox_detail);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS(klass), EditContactView, label_uri);
+}
+
+/**
+ * If a Person is specified, a view to edit the given Person will be created;
+ * if no Person is given (NULL), then a new Person object will be created when
+ * 'save' is clicked
+ */
+GtkWidget *
+edit_contact_view_new(ContactMethod *cm, Person *p)
+{
+    g_return_val_if_fail(cm, NULL);
+
+    gpointer self = g_object_new(EDIT_CONTACT_VIEW_TYPE, NULL);
+
+    EditContactViewPrivate *priv = EDIT_CONTACT_VIEW_GET_PRIVATE(self);
+
+    priv->cm = cm;
+    gtk_label_set_markup(
+        GTK_LABEL(priv->label_uri),
+        (QString("<b>") + cm->uri() + QString("</b>")).toUtf8().constData()
+    );
+
+    if (p) {
+        priv->person = p;
+
+        /* select the proper addressbook and prevent it from being modified */
+        if (p->collection() != NULL) {
+            auto model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->combobox_addressbook));
+            GtkTreeIter iter;
+            if (gtk_tree_model_get_iter_first(model, &iter)) {
+                GValue value = G_VALUE_INIT;
+                gtk_tree_model_get_value(model, &iter, 1, &value);
+                auto collection = (CollectionInterface *)g_value_get_pointer(&value);
+                while (collection != p->collection() && gtk_tree_model_iter_next(model, &iter)) {
+                    value = G_VALUE_INIT;
+                    gtk_tree_model_get_value(model, &iter, 1, &value);
+                    collection = (CollectionInterface *)g_value_get_pointer(&value);
+                }
+            }
+
+            gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combobox_addressbook), &iter);
+        }
+        gtk_widget_set_sensitive(priv->combobox_addressbook, FALSE);
+
+        /* set the name of the person, and prevent it from being edited */
+        gtk_entry_set_text(GTK_ENTRY(priv->entry_name), p->formattedName().toUtf8().constData());
+        g_object_set(G_OBJECT(priv->entry_name), "editable", FALSE, NULL);
+    }
+
+    return (GtkWidget *)self;
+}
diff --git a/src/editcontactview.h b/src/editcontactview.h
new file mode 100644
index 0000000..64c2833
--- /dev/null
+++ b/src/editcontactview.h
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (C) 2015 Savoir-faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ *  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 3 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, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *  Additional permission under GNU GPL version 3 section 7:
+ *
+ *  If you modify this program, or any covered work, by linking or
+ *  combining it with the OpenSSL project's OpenSSL library (or a
+ *  modified version of that library), containing parts covered by the
+ *  terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
+ *  grants you additional permission to convey the resulting work.
+ *  Corresponding Source for a non-source form of such a combination
+ *  shall include the source code for the parts of OpenSSL used as well
+ *  as that of the covered work.
+ */
+
+#ifndef _EDITCONTACTVIEW_H
+#define _EDITCONTACTVIEW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+class ContactMethod;
+class Person;
+
+#define EDIT_CONTACT_VIEW_TYPE            (edit_contact_view_get_type ())
+#define EDIT_CONTACT_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EDIT_CONTACT_VIEW_TYPE, EditContactView))
+#define EDIT_CONTACT_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), EDIT_CONTACT_VIEW_TYPE, EditContactViewClass))
+#define IS_EDIT_CONTACT_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), EDIT_CONTACT_VIEW_TYPE))
+#define IS_EDIT_CONTACT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EDIT_CONTACT_VIEW_TYPE))
+
+typedef struct _EditContactView      EditContactView;
+typedef struct _EditContactViewClass EditContactViewClass;
+
+GType      edit_contact_view_get_type(void) G_GNUC_CONST;
+
+/**
+ * If a Person is specified, a view to edit the given Person will be created;
+ * if no Person is given (NULL), then a new Person object will be created when
+ * 'save' is clicked
+ */
+GtkWidget *edit_contact_view_new     (ContactMethod *cm, Person *p);
+
+G_END_DECLS
+
+#endif /* _EDITCONTACTVIEW_H */
diff --git a/src/frequentcontactsview.cpp b/src/frequentcontactsview.cpp
index b636493..eca6cfe 100644
--- a/src/frequentcontactsview.cpp
+++ b/src/frequentcontactsview.cpp
@@ -194,7 +194,14 @@
      if (idx.isValid() && var_cm.isValid()) {
          if (auto contactmethod = var_cm.value<ContactMethod *>()) {
              if (!contact_method_has_contact(contactmethod)) {
-                 auto add_to = menu_item_contact_add_to(contactmethod, GTK_WIDGET(treeview));
+                 /* get rectangle */
+                 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);
              }
          }
diff --git a/src/historyview.cpp b/src/historyview.cpp
index 758391e..f53c603 100644
--- a/src/historyview.cpp
+++ b/src/historyview.cpp
@@ -200,7 +200,16 @@
         if (auto call = var_c.value<Call *>()) {
             auto contactmethod = call->peerContactMethod();
             if (!contact_method_has_contact(contactmethod)) {
-                auto add_to = menu_item_contact_add_to(contactmethod, GTK_WIDGET(treeview));
+                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);
             }
         }
diff --git a/src/utils/menus.cpp b/src/utils/menus.cpp
index a729bd3..ffb1ccc 100644
--- a/src/utils/menus.cpp
+++ b/src/utils/menus.cpp
@@ -31,7 +31,7 @@
 #include "menus.h"
 
 #include <contactmethod.h>
-#include "../createcontactdialog.h"
+#include "../contactpopover.h"
 
 /**
  * checks if the given contact method is already associated with a contact
@@ -45,41 +45,38 @@
 }
 
 static void
-create_new_contact(GtkWidget *item, ContactMethod *contactmethod)
+choose_contact(GtkWidget *item, ContactMethod *contactmethod)
 {
     // we get the parent widget which should be stored in the item object
-    GtkWidget *parent = GTK_WIDGET(g_object_get_data(G_OBJECT(item), "parent-widget"));
-    auto dialog = create_contact_dialog_new(contactmethod, parent);
-    gtk_dialog_run(GTK_DIALOG(dialog));
-    gtk_widget_destroy(dialog);
+    auto parent = g_object_get_data(G_OBJECT(item), "parent-widget");
+    auto rect = g_object_get_data(G_OBJECT(item), "parent-rectangle");
+
+    auto popover = contact_popover_new(contactmethod, GTK_WIDGET(parent), (GdkRectangle *)rect);
+
+    gtk_widget_show(popover);
 }
 
 /**
- * creates a menu item with 2 options:
- *  - create a new contact with the given contact method
- *  TODO: - add given contact method to existing contact
+ * creates a menu item allowing the adition of a contact method to a contact
  */
-GtkWidget * menu_item_contact_add_to(ContactMethod *cm, GtkWidget *parent)
+GtkWidget * menu_item_add_to_contact(ContactMethod *cm, GtkWidget *parent, const GdkRectangle *rect)
 {
     g_return_val_if_fail(cm, NULL);
 
-    auto add_to = gtk_menu_item_new_with_mnemonic("_Add to");
-
-    auto add_to_menu = gtk_menu_new();
-    gtk_menu_item_set_submenu(GTK_MENU_ITEM(add_to), add_to_menu);
-
-    /* TODO: uncomment when feature added */
-    // auto existing = gtk_menu_item_new_with_mnemonic("_Existing contact");
-    // gtk_menu_shell_append(GTK_MENU_SHELL(add_to_menu), existing);
-
-    auto new_contact = gtk_menu_item_new_with_mnemonic("_New contact");
-    gtk_menu_shell_append(GTK_MENU_SHELL(add_to_menu), new_contact);
+    auto add_to = gtk_menu_item_new_with_mnemonic("_Add to contact");
 
     /* save the parent widget in the item object, so we can retrieve
      * it in the callback */
-    g_object_set_data(G_OBJECT(new_contact), "parent-widget", parent);
+    g_object_set_data(G_OBJECT(add_to), "parent-widget", parent);
 
-    g_signal_connect(new_contact, "activate", G_CALLBACK(create_new_contact), cm);
+    GdkRectangle *copy_rect = NULL;
+    if (rect) {
+        copy_rect = g_new0(GdkRectangle, 1);
+        memcpy(copy_rect, rect, sizeof(GdkRectangle));
+    }
+    g_object_set_data_full(G_OBJECT(add_to), "parent-rectangle", copy_rect, (GDestroyNotify)g_free);
+
+    g_signal_connect(add_to, "activate", G_CALLBACK(choose_contact), cm);
 
     return add_to;
 }
diff --git a/src/utils/menus.h b/src/utils/menus.h
index 6e63dc1..359fbf7 100644
--- a/src/utils/menus.h
+++ b/src/utils/menus.h
@@ -43,11 +43,9 @@
 gboolean    contact_method_has_contact(ContactMethod *cm);
 
 /**
- * creates a menu item with 2 options:
- *  - create a new contact with the given contact method
- *  TODO: - add given contact method to existing contact
+ * creates a menu item allowing the adition of a contact method to a contact
  */
-GtkWidget * menu_item_contact_add_to(ContactMethod *cm, GtkWidget *parent);
+GtkWidget * menu_item_add_to_contact(ContactMethod *cm, GtkWidget *parent, const GdkRectangle *rect);
 
 G_END_DECLS
 
diff --git a/ui/choosecontactview.ui b/ui/choosecontactview.ui
new file mode 100644
index 0000000..79c39bb
--- /dev/null
+++ b/ui/choosecontactview.ui
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <template class="ChooseContactView" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkScrolledWindow" id="scrolledwindow_choose_contact">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="shadow_type">none</property>
+        <property name="min-content-width">300</property>
+        <property name="min-content-height">300</property>
+        <child>
+          <object class="GtkTreeView" id="treeview_choose_contact">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="headers-visible">False</property>
+            <!-- we  hide the expanders since there is no need to show the contact methods -->
+            <property name="show-expanders">False</property>
+            <child internal-child="selection">
+              <object class="GtkTreeSelection" id="treeview-selection1"/>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="button_create_contact">
+        <property name="label" translatable="yes">Create New</property>
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="receives_default">True</property>
+        <property name="always_show_image">True</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/ui/createcontactdialog.ui b/ui/createcontactdialog.ui
deleted file mode 100644
index a940b03..0000000
--- a/ui/createcontactdialog.ui
+++ /dev/null
@@ -1,145 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<interface>
-  <requires lib="gtk+" version="3.10"/>
-  <template class="CreateContactDialog" parent="GtkDialog">
-    <property name="title" translatable="yes">Create New Contact</property>
-    <property name="type_hint">dialog</property>
-    <property name="modal">True</property>
-    <property name="destroy_with_parent">True</property>
-    <property name="window_position">center-on-parent</property>
-    <child internal-child="vbox">
-      <object class="GtkBox" id="dialog-vbox1">
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <object class="GtkButtonBox" id="dialog-action_area1">
-            <property name="layout_style">end</property>
-            <child>
-              <object class="GtkButton" id="button_save">
-                <property name="label" translatable="yes">Save</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-              </object>
-              <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkButton" id="button_cancel">
-                <property name="label" translatable="yes">Cancel</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-              </object>
-              <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">False</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkGrid" id="grid_add_contact">
-            <property name="visible">True</property>
-            <property name="row_spacing">10</property>
-            <property name="column_spacing">10</property>
-            <property name="border_width">10</property>
-            <child>
-              <object class="GtkLabel" id="label_addressbook">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="halign">end</property>
-                <property name="label" translatable="yes">Addressbook:</property>
-                <property name="mnemonic_widget">combobox_addressbook</property>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkComboBox" id="combobox_addressbook">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="top_attach">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel" id="label_name">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="halign">end</property>
-                <property name="label" translatable="yes">Name:</property>
-                <property name="mnemonic_widget">entry_name</property>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkEntry" id="entry_name">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="hexpand">True</property>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="top_attach">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkComboBox" id="combobox_detail">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="halign">end</property>
-                <property name="has_tooltip">True</property>
-                <property name="tooltip_text" translatable="yes">Select the detail which will be used for this number</property>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="comboboxtext_detail-atkobject">
-                    <property name="AtkObject::accessible-name" translatable="yes">Contact detail</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkEntry" id="entry_contactmethod">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="hexpand">True</property>
-                <property name="xalign">0.5</property>
-                <property name="editable">False</property>
-                <child internal-child="accessible">
-                  <object class="AtkObject" id="entry_contactmethod-atkobject">
-                    <property name="AtkObject::accessible-name" translatable="yes">Number or Ring ID</property>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="top_attach">2</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-          </packing>
-        </child>
-      </object>
-    </child>
-  </template>
-</interface>
diff --git a/ui/editcontactview.ui b/ui/editcontactview.ui
new file mode 100644
index 0000000..7da1b6a
--- /dev/null
+++ b/ui/editcontactview.ui
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <template class="EditContactView" parent="GtkGrid">
+    <property name="visible">True</property>
+    <property name="row-spacing">10</property>
+    <property name="column-spacing">10</property>
+    <child>
+      <object class="GtkComboBox" id="combobox_addressbook">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="tooltip_text" translatable="yes">Select addressbook</property>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="combobox_addressbook-atkobject">
+            <property name="AtkObject::accessible-name" translatable="yes">Addressbook</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">0</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkEntry" id="entry_name">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="placeholder-text" translatable="yes">Name</property>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="entry_name-atkobject">
+            <property name="AtkObject::accessible-name" translatable="yes">Contact name</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">1</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkComboBox" id="combobox_detail">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="has_tooltip">True</property>
+        <property name="tooltip_text" translatable="yes">Select how this number will be categorized</property>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="comboboxtext_detail-atkobject">
+            <property name="AtkObject::accessible-name" translatable="yes">Number category</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_uri">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="ellipsize">PANGO_ELLIPSIZE_END</property>
+        <property name="halign">start</property>
+        <property name="selectable">True</property>
+        <property name="tooltip_text" translatable="yes">Number or Ring ID to be added</property>
+        <property name="width-chars">20</property>
+        <property name="max-width-chars">20</property>
+        <property name="xalign">0.5</property>
+        <child internal-child="accessible">
+          <object class="AtkObject" id="label_uri-atkobject">
+            <property name="AtkObject::accessible-name" translatable="yes">Number or Ring ID</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left-attach">1</property>
+        <property name="top-attach">2</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="button_save">
+        <property name="label" translatable="yes">Save</property>
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="receives_default">True</property>
+      </object>
+      <packing>
+        <property name="left-attach">0</property>
+        <property name="top-attach">3</property>
+        <property name="width">2</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/ui/ui.gresource.xml b/ui/ui.gresource.xml
index dd2fff9..4dc2c07 100644
--- a/ui/ui.gresource.xml
+++ b/ui/ui.gresource.xml
@@ -13,6 +13,7 @@
     <file preprocess="xml-stripblanks">accountadvancedtab.ui</file>
     <file preprocess="xml-stripblanks">generalsettingsview.ui</file>
     <file preprocess="xml-stripblanks">accountsecuritytab.ui</file>
-    <file preprocess="xml-stripblanks">createcontactdialog.ui</file>
+    <file preprocess="xml-stripblanks">editcontactview.ui</file>
+    <file preprocess="xml-stripblanks">choosecontactview.ui</file>
   </gresource>
 </gresources>