Implement Multi-Device

This patch implements multi-device support:

- The account creation wizzard has now two options "Existing Ring
  account" and "New Ring account".

  "Existing Ring account": Allows for fetching a Ring account archive
  from the DHT. Requires pin and password.

  "New Ring account": This is the previously existing wizard. It was
  modified to ask for a password which will be used to encrypt the
  account archive. This password is then required for exporting the
  account on the Ring.

- Creating a new Ring account with the "+" button now triggers the
  account creation wizard.

- The account menu now has a "devices" tab. This tab contains a table
  with device names (currently a short hash) and device ids (a long
  hash).

  In the "devices" tab, there is an "add device" button which allows
  for exporting the current account to the Ring, giving a pin to the
  user.

- When the client encounters old-format accounts, it will trigger a
  migration popup which asks the user for a password. This password will
  be used to create an archive and encrypt it. One password will be
  asked for each Ring account to migrate.

Change-Id: I3d52b2b7ca4f82cb477ee294c962b5d50d5c6a04
Tuleap: #896
diff --git a/src/accountcreationwizard.cpp b/src/accountcreationwizard.cpp
new file mode 100644
index 0000000..507f47e
--- /dev/null
+++ b/src/accountcreationwizard.cpp
@@ -0,0 +1,553 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Alexandre Viau <alexandre.viau@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.
+ */
+
+
+// GTK+ related
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+// LRC
+#include <account.h>
+#include <codecmodel.h>
+#include <profilemodel.h>
+#include <profile.h>
+#include <accountmodel.h>
+#include <personmodel.h>
+
+// Ring Client
+#include "utils/models.h"
+#include "avatarmanipulation.h"
+#include "accountcreationwizard.h"
+
+
+struct _AccountCreationWizard
+{
+    GtkBox parent;
+};
+
+struct _AccountCreationWizardClass
+{
+    GtkBoxClass parent_class;
+};
+
+typedef struct _AccountCreationWizardPrivate AccountCreationWizardPrivate;
+
+struct _AccountCreationWizardPrivate
+{
+    GtkWidget *stack_account_creation;
+    QMetaObject::Connection account_state_changed;
+
+    /* choose_account_type_vbox */
+    GtkWidget *choose_account_type_vbox;
+    GtkWidget *choose_account_type_ring_logo;
+    GtkWidget *button_new_account;
+    GtkWidget *button_existing_account;
+    GtkWidget *button_wizard_cancel;
+
+    /* existing account */
+    GtkWidget *existing_account;
+    GtkWidget *button_existing_account_next;
+    GtkWidget *button_existing_account_previous;
+    GtkWidget *entry_existing_account_pin;
+    GtkWidget *entry_existing_account_password;
+    GtkWidget *retrieving_account;
+
+    /* account creation */
+    GtkWidget *account_creation;
+    GtkWidget *vbox_account_creation_entry;
+    GtkWidget *entry_alias;
+    GtkWidget *entry_password;
+    GtkWidget *entry_password_confirm;
+    GtkWidget *label_default_name;
+    GtkWidget *label_paceholder;
+    GtkWidget *button_account_creation_next;
+    GtkWidget *button_account_creation_previous;
+    GtkWidget *box_avatarselection;
+    GtkWidget *avatar_manipulation;
+    GtkWidget *label_password_error;
+
+    /* generating_account_spinner */
+    GtkWidget *vbox_generating_account_spinner;
+
+    /* error_view */
+    GtkWidget *error_view;
+    GtkWidget *button_error_view_ok;
+
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountCreationWizard, account_creation_wizard, GTK_TYPE_BOX);
+
+#define ACCOUNT_CREATION_WIZARD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizardPrivate))
+
+/* signals */
+enum {
+    ACCOUNT_CREATION_CANCELED,
+    ACCOUNT_CREATION_COMPLETED,
+    LAST_SIGNAL
+};
+
+static guint account_creation_wizard_signals[LAST_SIGNAL] = { 0 };
+
+static void
+destroy_avatar_manipulation(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    /* make sure the AvatarManipulation widget is destroyed so the VideoWidget inside of it is too;
+     * NOTE: destorying its parent (box_avatarselection) first will cause a mystery 'BadDrawable'
+     * crash due to X error */
+    if (priv->avatar_manipulation)
+    {
+        gtk_container_remove(GTK_CONTAINER(priv->box_avatarselection), priv->avatar_manipulation);
+        priv->avatar_manipulation = nullptr;
+    }
+}
+
+static void
+account_creation_wizard_dispose(GObject *object)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(object);
+    destroy_avatar_manipulation(ACCOUNT_CREATION_WIZARD(object));
+    QObject::disconnect(priv->account_state_changed);
+    G_OBJECT_CLASS(account_creation_wizard_parent_class)->dispose(object);
+}
+
+static void
+account_creation_wizard_init(AccountCreationWizard *view)
+{
+    gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_creation_wizard_class_init(AccountCreationWizardClass *klass)
+{
+    G_OBJECT_CLASS(klass)->dispose = account_creation_wizard_dispose;
+
+    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+                                                "/cx/ring/RingGnome/accountcreationwizard.ui");
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, stack_account_creation);
+
+    /* choose_account_type_vbox */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_vbox);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, choose_account_type_ring_logo);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_new_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_existing_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_wizard_cancel);
+
+    /* existing account */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, existing_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_existing_account_next);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_existing_account_previous);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_pin);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_existing_account_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, retrieving_account);
+
+    /* account creation */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, account_creation);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_account_creation_entry);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_alias);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, entry_password_confirm);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_default_name);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_paceholder);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_next);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_account_creation_previous);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, box_avatarselection);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, label_password_error);
+
+    /* generating_account_spinner */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, vbox_generating_account_spinner);
+
+    /* error view */
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, error_view);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountCreationWizard, button_error_view_ok);
+
+    /* add signals */
+    account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED] = g_signal_new("account-creation-completed",
+                 G_TYPE_FROM_CLASS(klass),
+                 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+                 0,
+                 nullptr,
+                 nullptr,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+
+    account_creation_wizard_signals[ACCOUNT_CREATION_CANCELED] = g_signal_new("account-creation-canceled",
+                 G_TYPE_FROM_CLASS(klass),
+                 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
+                 0,
+                 nullptr,
+                 nullptr,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+}
+
+static void
+show_error_view(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->error_view);
+}
+
+static gboolean
+create_ring_account(AccountCreationWizard *view,
+                    gchar *alias,
+                    gchar *password,
+                    gchar *pin)
+{
+    g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(view), G_SOURCE_REMOVE);
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    g_object_ref(view); // ref so its not desroyed too early
+
+    /* create account and set UPnP enabled, as its not by default in the daemon */
+    Account *account = nullptr;
+
+    /* get profile (if so) */
+    auto profile = ProfileModel::instance().selectedProfile();
+
+    if (alias && strlen(alias) > 0) {
+        account = AccountModel::instance().add(alias, Account::Protocol::RING);
+        if(profile && AccountModel::instance().size() == 1)
+        {
+            profile->person()->setFormattedName(alias);
+        }
+    } else {
+        auto unknown_alias = C_("The default username / account alias, if none is set by the user", "Unknown");
+        account = AccountModel::instance().add(unknown_alias, Account::Protocol::RING);
+        if (profile && AccountModel::instance().size() == 1)
+        {
+            profile->person()->setFormattedName(unknown_alias);
+        }
+    }
+
+    /* Set the archive password */
+    account->setArchivePassword(password);
+
+    /* Set the archive pin (existng accounts) */
+    if(pin)
+    {
+        account->setArchivePin(pin);
+    }
+
+    account->setDisplayName(alias); // set the display name to the same as the alias
+
+    account->setUpnpEnabled(TRUE);
+
+    /* show error window if the account errors */
+    priv->account_state_changed = QObject::connect(
+        account,
+        &Account::stateChanged,
+        [=] (Account::RegistrationState state) {
+            switch(state)
+            {
+                case Account::RegistrationState::ERROR:
+                {
+                    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+                    QObject::disconnect(priv->account_state_changed);
+                    show_error_view(view);
+                    g_object_unref(view);
+                    break;
+                }
+                case Account::RegistrationState::READY:
+                case Account::RegistrationState::TRYING:
+                case Account::RegistrationState::UNREGISTERED:
+                {
+                    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+                    QObject::disconnect(priv->account_state_changed);
+
+                    account << Account::EditAction::RELOAD;
+                    g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_COMPLETED], 0);
+                    g_object_unref(view);
+                    break;
+                }
+                case Account::RegistrationState::INITIALIZING:
+                case Account::RegistrationState::COUNT__:
+                {
+                    // Keep waiting...
+                    break;
+                }
+            }
+        }
+    );
+
+    account->performAction(Account::EditAction::SAVE);
+    profile->save();
+
+    return G_SOURCE_REMOVE;
+}
+
+static gboolean
+create_new_ring_account(AccountCreationWizard *win)
+{
+    g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    gchar *alias = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_alias)));
+
+    gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_password)));
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_password_confirm), "");
+
+    auto status = create_ring_account(win, alias, password, NULL);
+
+    g_free(alias);
+    g_free(password);
+
+    return status;
+}
+
+static gboolean
+create_existing_ring_account(AccountCreationWizard *win)
+{
+    g_return_val_if_fail(IS_ACCOUNT_CREATION_WIZARD(win), G_SOURCE_REMOVE);
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    gchar *password = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_password)));
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_password), "");
+
+    gchar *pin = g_strdup(gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin)));
+    gtk_entry_set_text(GTK_ENTRY(priv->entry_existing_account_pin), "");
+
+    auto status = create_ring_account(win, NULL, password, pin);
+
+    g_free(password);
+    g_free(pin);
+
+    return status;
+}
+
+
+static void
+alias_entry_changed(GtkEditable *entry, AccountCreationWizard *win)
+{
+    g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(win));
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (!alias || strlen(alias) == 0) {
+        gtk_widget_show(priv->label_default_name);
+        gtk_widget_hide(priv->label_paceholder);
+    } else {
+        gtk_widget_hide(priv->label_default_name);
+        gtk_widget_show(priv->label_paceholder);
+    }
+}
+
+static void
+show_generating_account_spinner(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->vbox_generating_account_spinner);
+}
+
+static void
+account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    /* Check for correct password */
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
+
+    if (g_strcmp0(password, password_confirm) != 0)
+    {
+        gtk_widget_show(priv->label_password_error);
+        return;
+    }
+
+    show_generating_account_spinner(win);
+
+    /* now create account after a short timeout so that the the save doesn't
+     * happen freeze the client before the widget changes happen;
+     * the timeout function should then display the next step in account creation
+     */
+    g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_new_ring_account, win, NULL);
+}
+
+static void
+show_retrieving_account(AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->retrieving_account);
+}
+
+static void
+existing_account_next_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *win)
+{
+    show_retrieving_account(win);
+
+    /* now create account after a short timeout so that the the save doesn't
+     * happen freeze the client before the widget changes happen;
+     * the timeout function should then display the next step in account creation
+     */
+    g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)create_existing_ring_account, win, NULL);
+}
+
+static void
+entry_alias_activated(GtkEntry *entry, AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    const gchar *alias = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (strlen(alias) > 0)
+        gtk_button_clicked(GTK_BUTTON(priv->button_account_creation_next));
+}
+
+
+static void
+show_choose_account_type(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->choose_account_type_vbox);
+}
+
+static void
+show_existing_account(AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->existing_account);
+}
+
+static void
+show_account_creation(AccountCreationWizard *win)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(win);
+
+    /* avatar manipulation widget */
+    if (!priv->avatar_manipulation)
+    {
+        priv->avatar_manipulation = avatar_manipulation_new_from_wizard();
+        gtk_box_pack_start(GTK_BOX(priv->box_avatarselection), priv->avatar_manipulation, true, true, 0);
+    }
+
+    gtk_widget_hide(priv->label_password_error);
+
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_creation), priv->account_creation);
+}
+
+static void
+wizard_cancel_clicked(G_GNUC_UNUSED GtkButton *button, AccountCreationWizard *view)
+{
+    g_signal_emit(G_OBJECT(view), account_creation_wizard_signals[ACCOUNT_CREATION_CANCELED], 0);
+}
+
+static void
+entries_existing_account_changed(G_GNUC_UNUSED GtkEntry *entry, AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    const gchar *pin = gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_pin));
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_existing_account_password));
+
+    if (strlen(pin) > 0 && strlen(password) > 0)
+    {
+        gtk_widget_set_sensitive(priv->button_existing_account_next, TRUE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive(priv->button_existing_account_next, FALSE);
+    }
+}
+
+static void
+entries_new_account_changed(G_GNUC_UNUSED GtkEntry *entry, AccountCreationWizard *view)
+{
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm));
+
+    if (strlen(password) > 0 && strlen(password_confirm) > 0)
+    {
+        gtk_widget_set_sensitive(priv->button_account_creation_next, TRUE);
+    }
+    else
+    {
+        gtk_widget_set_sensitive(priv->button_account_creation_next, FALSE);
+    }
+}
+
+static void
+build_creation_wizard_view(AccountCreationWizard *view, bool show_cancel_button)
+{
+    g_return_if_fail(IS_ACCOUNT_CREATION_WIZARD(view));
+    AccountCreationWizardPrivate *priv = ACCOUNT_CREATION_WIZARD_GET_PRIVATE(view);
+
+    /* set ring logo */
+    GError *error = NULL;
+    GdkPixbuf* logo_ring = gdk_pixbuf_new_from_resource_at_scale("/cx/ring/RingGnome/ring-logo-blue",
+                                                                  -1, 50, TRUE, &error);
+    if (logo_ring == NULL) {
+        g_debug("Could not load logo: %s", error->message);
+        g_clear_error(&error);
+    } else
+        gtk_image_set_from_pixbuf(GTK_IMAGE(priv->choose_account_type_ring_logo), logo_ring);
+
+
+    /* use the real name / username of the logged in user as the default */
+    const char* real_name = g_get_real_name();
+    const char* user_name = g_get_user_name();
+    g_debug("real_name = %s",real_name);
+    g_debug("user_name = %s",user_name);
+
+    /* check first if the real name was determined */
+    if (g_strcmp0 (real_name,"Unknown") != 0)
+        gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), real_name);
+    else
+        gtk_entry_set_text(GTK_ENTRY(priv->entry_alias), user_name);
+
+    /* cancel button */
+    gtk_widget_set_visible(priv->button_wizard_cancel, show_cancel_button);
+
+    /* choose_account_type signals */
+    g_signal_connect_swapped(priv->button_new_account, "clicked", G_CALLBACK(show_account_creation), view);
+    g_signal_connect_swapped(priv->button_existing_account, "clicked", G_CALLBACK(show_existing_account), view);
+    g_signal_connect(priv->button_wizard_cancel, "clicked", G_CALLBACK(wizard_cancel_clicked), view);
+
+    /* account_creation signals */
+    g_signal_connect_swapped(priv->button_account_creation_previous, "clicked", G_CALLBACK(show_choose_account_type), view);
+    g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), view);
+    g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), view);
+    g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), view);
+    g_signal_connect(priv->entry_password, "changed", G_CALLBACK(entries_new_account_changed), view);
+    g_signal_connect(priv->entry_password_confirm, "changed", G_CALLBACK(entries_new_account_changed), view);
+
+    /* existing_account singals */
+    g_signal_connect_swapped(priv->button_existing_account_previous, "clicked", G_CALLBACK(show_choose_account_type), view);
+    g_signal_connect(priv->button_existing_account_next, "clicked", G_CALLBACK(existing_account_next_clicked), view);
+    g_signal_connect(priv->entry_existing_account_pin, "changed", G_CALLBACK(entries_existing_account_changed), view);
+    g_signal_connect(priv->entry_existing_account_password, "changed", G_CALLBACK(entries_existing_account_changed), view);
+
+    /* error_view signals */
+    g_signal_connect_swapped(priv->button_error_view_ok, "clicked", G_CALLBACK(show_choose_account_type), view);
+
+    show_choose_account_type(view);
+}
+
+GtkWidget *
+account_creation_wizard_new(bool show_cancel_button)
+{
+    gpointer view = g_object_new(ACCOUNT_CREATION_WIZARD_TYPE, NULL);
+
+    build_creation_wizard_view(ACCOUNT_CREATION_WIZARD(view), show_cancel_button);
+    return (GtkWidget *)view;
+}