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/accountview.cpp b/src/accountview.cpp
index 9ba34f1..8322465 100644
--- a/src/accountview.cpp
+++ b/src/accountview.cpp
@@ -29,15 +29,19 @@
 #include "models/gtkqtreemodel.h"
 #include "models/activeitemproxymodel.h"
 #include "accountgeneraltab.h"
+#include "accountcreationwizard.h"
 #include "accountaudiotab.h"
 #include "accountvideotab.h"
 #include "accountadvancedtab.h"
 #include "accountsecuritytab.h"
+#include "accountdevicestab.h"
 #include "dialogs.h"
 #include <glib/gprintf.h>
 #include "utils/models.h"
 #include "accountimportexportview.h"
 
+static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
+
 struct _AccountView
 {
     GtkPaned parent;
@@ -131,6 +135,16 @@
     AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
 
     auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+    if(IS_ACCOUNT_CREATION_WIZARD(old_view))
+    {
+        /* When using the account creation wizard, The user might create
+         * accounts that will be deleted by the daemon. The fact that the
+         * selection has changed should be ignored. The account creation wizard
+         * should be responsible to remove itself of the stack and then call
+         * account_selection_changed.
+         */
+        return;
+    }
 
     QModelIndex account_idx = get_index_from_selection(selection);
     if (!account_idx.isValid()) {
@@ -176,6 +190,10 @@
         gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
                                  security_tab,
                                  gtk_label_new(C_("Account settings", "Security")));
+        auto devices_tab = create_scrolled_account_view(account_devices_tab_new(account));
+        gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
+                                 devices_tab,
+                                 gtk_label_new(C_("Account settings", "Devices")));
 
         gtk_widget_show_all(hbox_account);
         /* set the tab displayed to the same as the prev account selected */
@@ -296,6 +314,54 @@
 }
 
 static void
+hide_account_creation_wizard(AccountView *view)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(view));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+    auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+    if (IS_ACCOUNT_CREATION_WIZARD(old_view))
+    {
+        gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+    }
+
+    auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
+    account_selection_changed(selection, view);
+}
+
+static void
+show_account_creation_wizard(AccountView *view)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(view));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+    auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+
+    auto account_creation_wizard = gtk_stack_get_child_by_name(
+        GTK_STACK(priv->stack_account),
+        ACCOUNT_CREATION_WIZARD_VIEW_NAME
+    );
+
+    if (!account_creation_wizard)
+    {
+        account_creation_wizard = account_creation_wizard_new(true);
+        gtk_stack_add_named(GTK_STACK(priv->stack_account),
+                            account_creation_wizard,
+                            ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+        g_signal_connect_swapped(account_creation_wizard, "account-creation-completed", G_CALLBACK(hide_account_creation_wizard), view);
+        g_signal_connect_swapped(account_creation_wizard, "account-creation-canceled", G_CALLBACK(hide_account_creation_wizard), view);
+    }
+
+    gtk_widget_show(account_creation_wizard);
+
+    gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_account), ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+
+    /* remove the old view */
+    if (old_view)
+        gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+}
+
+static void
 add_account(G_GNUC_UNUSED GtkWidget *entry, AccountView *view)
 {
     g_return_if_fail(IS_ACCOUNT_VIEW(view));
@@ -311,19 +377,26 @@
         if (protocol_idx.isValid()) {
             protocol_idx = priv->active_protocols->mapToSource(protocol_idx);
 
-            /* show working dialog in case save operation takes time */
-            GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
-            gtk_window_present(GTK_WINDOW(working));
+            Account::Protocol protocol = qvariant_cast<Account::Protocol>(protocol_idx.data((int)ProtocolModel::Role::Protocol));
+            if (protocol == Account::Protocol::RING)
+            {
+                show_account_creation_wizard(view);
+            }
+            else
+            {
+                /* show working dialog in case save operation takes time */
+                GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
+                gtk_window_present(GTK_WINDOW(working));
 
-            auto account = AccountModel::instance().add(QString(_("New Account")), protocol_idx);
-            if (account->protocol() == Account::Protocol::RING)
-                account->setDisplayName(_("New Account"));
+                AccountModel::instance().add(QString(_("New Account")), protocol_idx);
 
-            /* now save after a short timeout to make sure that
-             * the save doesn't happen before the "working" dialog is presented
-             * the timeout function should destroy the "working" dialog when done saving
-             */
-            g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
+                /* now save after a short timeout to make sure that
+                 * the save doesn't happen before the "working" dialog is presented
+                 * the timeout function should destroy the "working" dialog when done saving
+                 */
+                g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
+            }
+
         }
     }
 }
@@ -365,6 +438,7 @@
                     display_state = g_strdup_printf("<span fgcolor=\"gray\">%s</span>", escaped_state);
                 break;
                 case Account::RegistrationState::TRYING:
+                case Account::RegistrationState::INITIALIZING:
                     display_state = g_strdup_printf("<span fgcolor=\"orange\">%s</span>", escaped_state);
                 break;
                 case Account::RegistrationState::ERROR: