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;
+}
diff --git a/src/accountcreationwizard.h b/src/accountcreationwizard.h
new file mode 100644
index 0000000..c47d86c
--- /dev/null
+++ b/src/accountcreationwizard.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_CREATION_WIZARD_TYPE (account_creation_wizard_get_type ())
+#define ACCOUNT_CREATION_WIZARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizard))
+#define ACCOUNT_CREATION_WIZARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_CREATION_WIZARD_TYPE, AccountCreationWizardClass))
+#define IS_ACCOUNT_CREATION_WIZARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_CREATION_WIZARD_TYPE))
+#define IS_ACCOUNT_CREATION_WIZARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_CREATION_WIZARD_TYPE))
+
+typedef struct _AccountCreationWizard AccountCreationWizard;
+typedef struct _AccountCreationWizardClass AccountCreationWizardClass;
+
+GType account_creation_wizard_get_type (void) G_GNUC_CONST;
+GtkWidget *account_creation_wizard_new (bool cancel_button);
+
+G_END_DECLS
diff --git a/src/accountdevicestab.cpp b/src/accountdevicestab.cpp
new file mode 100644
index 0000000..fbf8f18
--- /dev/null
+++ b/src/accountdevicestab.cpp
@@ -0,0 +1,272 @@
+/*
+ * 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 <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+// LRC
+#include <account.h>
+#include <codecmodel.h>
+#include "ringdevicemodel.h"
+#include "ringdevice.h"
+
+// Ring Client
+#include "accountdevicestab.h"
+#include "models/gtkqtreemodel.h"
+#include "utils/models.h"
+
+struct _AccountDevicesTab
+{
+ GtkBox parent;
+};
+
+struct _AccountDevicesTabClass
+{
+ GtkBoxClass parent_class;
+};
+
+typedef struct _AccountDevicesTabPrivate AccountDevicesTabPrivate;
+
+struct _AccountDevicesTabPrivate
+{
+ Account *account;
+ GtkWidget *stack_account_devices;
+ QMetaObject::Connection export_on_ring_ended;
+
+ /* generated_pin view */
+ GtkWidget *generated_pin;
+ GtkWidget *label_generated_pin;
+ GtkWidget *button_generated_pin_ok;
+
+ /* manage_devices view */
+ GtkWidget *manage_devices;
+ GtkWidget *label_device_id;
+ GtkWidget *treeview_devices;
+ GtkWidget *button_add_device;
+
+ /* add_device view */
+ GtkWidget *add_device;
+ GtkWidget *button_export_on_the_ring;
+ GtkWidget *button_add_device_cancel;
+ GtkWidget *entry_password;
+
+ /* generating account spinner */
+ GtkWidget *vbox_generating_pin_spinner;
+
+ /* export on ring error */
+ GtkWidget *export_on_ring_error;
+ GtkWidget *label_export_on_ring_error;
+ GtkWidget* button_export_on_ring_error_ok;
+
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountDevicesTab, account_devices_tab, GTK_TYPE_BOX);
+
+#define ACCOUNT_DEVICES_TAB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTabPrivate))
+
+static void
+account_devices_tab_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(account_devices_tab_parent_class)->dispose(object);
+}
+
+static void
+account_devices_tab_init(AccountDevicesTab *view)
+{
+ gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_devices_tab_class_init(AccountDevicesTabClass *klass)
+{
+ G_OBJECT_CLASS(klass)->dispose = account_devices_tab_dispose;
+
+ gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+ "/cx/ring/RingGnome/accountdevicestab.ui");
+
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, stack_account_devices);
+
+ /* generated_pin view */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, generated_pin);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_generated_pin);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_generated_pin_ok);
+
+ /* manage_devices view */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, manage_devices);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_device_id);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, treeview_devices);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_add_device);
+
+ /* add_device view */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, add_device);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_export_on_the_ring);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_add_device_cancel);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, entry_password);
+
+ /* generating pin spinner */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, vbox_generating_pin_spinner);
+
+ /* export on ring error */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, export_on_ring_error);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, label_export_on_ring_error);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountDevicesTab, button_export_on_ring_error_ok);
+}
+
+static void
+show_manage_devices_view(AccountDevicesTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->manage_devices);
+}
+
+static void
+show_add_device_view(AccountDevicesTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->add_device);
+}
+
+static void
+show_generated_pin_view(AccountDevicesTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+ gtk_widget_show(priv->generated_pin);
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->generated_pin);
+}
+
+static void
+show_generating_pin_spinner(AccountDevicesTab *view)
+{
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->vbox_generating_pin_spinner);
+}
+
+static void
+show_export_on_ring_error(AccountDevicesTab *view)
+{
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->export_on_ring_error);
+}
+
+static void
+export_on_the_ring_clicked(G_GNUC_UNUSED GtkButton *button, AccountDevicesTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+
+ auto password = QString(gtk_entry_get_text(GTK_ENTRY(priv->entry_password)));
+ gtk_entry_set_text(GTK_ENTRY(priv->entry_password), "");
+
+ priv->export_on_ring_ended = QObject::connect(
+ priv->account,
+ &Account::exportOnRingEnded,
+ [=] (Account::ExportOnRingStatus status, QString pin) {
+ QObject::disconnect(priv->export_on_ring_ended);
+ switch (status)
+ {
+ case Account::ExportOnRingStatus::SUCCESS:
+ {
+ gtk_label_set_text(GTK_LABEL(priv->label_generated_pin), pin.toStdString().c_str());
+ show_generated_pin_view(view);
+ break;
+ }
+ case Account::ExportOnRingStatus::WRONG_PASSWORD:
+ {
+ gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Bad password"));
+ show_export_on_ring_error(view);
+ break;
+ }
+ case Account::ExportOnRingStatus::NETWORK_ERROR:
+ {
+ gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Network error, try again"));
+ show_export_on_ring_error(view);
+ break;
+ }
+ }
+ }
+ );
+
+ show_generating_pin_spinner(view);
+ if (!priv->account->exportOnRing(password))
+ {
+ QObject::disconnect(priv->export_on_ring_ended);
+ gtk_label_set_text(GTK_LABEL(priv->label_export_on_ring_error), _("Could not initiate export to the Ring, try again"));
+ g_debug("Could not initiate exportOnRing operation");
+ show_export_on_ring_error(view);
+ }
+}
+
+static void
+build_tab_view(AccountDevicesTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_DEVICES_TAB(view));
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+
+ g_signal_connect_swapped(priv->button_add_device, "clicked", G_CALLBACK(show_add_device_view), view);
+ g_signal_connect_swapped(priv->button_add_device_cancel, "clicked", G_CALLBACK(show_manage_devices_view), view);
+ g_signal_connect(priv->button_export_on_the_ring, "clicked", G_CALLBACK(export_on_the_ring_clicked), view);
+ g_signal_connect_swapped(priv->button_generated_pin_ok, "clicked", G_CALLBACK(show_manage_devices_view), view);
+ g_signal_connect_swapped(priv->button_export_on_ring_error_ok, "clicked", G_CALLBACK(show_add_device_view), view);
+
+ gtk_label_set_text(GTK_LABEL(priv->label_device_id), priv->account->deviceId().toUtf8().constData());
+
+ /* treeview_devices */
+ auto *devices_model = gtk_q_tree_model_new(
+ (QAbstractItemModel*) priv->account->ringDeviceModel(),
+ 2,
+ RingDevice::Column::Name, Qt::DisplayRole, G_TYPE_STRING,
+ RingDevice::Column::Id, Qt::DisplayRole, G_TYPE_STRING);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_devices), GTK_TREE_MODEL(devices_model));
+
+ GtkCellRenderer* renderer;
+ GtkTreeViewColumn* column;
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(C_("Device Name Column", "Name"), renderer, "text", 0, NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_devices), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(C_("Device ID Column", "ID"), renderer, "text", 1, NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_devices), column);
+
+ /* Show manage-devices view */
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_devices), priv->manage_devices);
+}
+
+GtkWidget *
+account_devices_tab_new(Account *account)
+{
+ g_return_val_if_fail(account != NULL, NULL);
+
+ gpointer view = g_object_new(ACCOUNT_DEVICES_TAB_TYPE, NULL);
+
+ AccountDevicesTabPrivate *priv = ACCOUNT_DEVICES_TAB_GET_PRIVATE(view);
+ priv->account = account;
+
+ build_tab_view(ACCOUNT_DEVICES_TAB(view));
+
+ return (GtkWidget *)view;
+}
diff --git a/src/accountdevicestab.h b/src/accountdevicestab.h
new file mode 100644
index 0000000..79b9209
--- /dev/null
+++ b/src/accountdevicestab.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+class Account;
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_DEVICES_TAB_TYPE (account_devices_tab_get_type ())
+#define ACCOUNT_DEVICES_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTab))
+#define ACCOUNT_DEVICES_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_DEVICES_TAB_TYPE, AccountDevicesTabClass))
+#define IS_ACCOUNT_DEVICES_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_DEVICES_TAB_TYPE))
+#define IS_ACCOUNT_DEVICES_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_DEVICES_TAB_TYPE))
+
+typedef struct _AccountDevicesTab AccountDevicesTab;
+typedef struct _AccountDevicesTabClass AccountDevicesTabClass;
+
+GType account_devices_tab_get_type (void) G_GNUC_CONST;
+GtkWidget *account_devices_tab_new (Account *account);
+
+G_END_DECLS
diff --git a/src/accountmigrationview.cpp b/src/accountmigrationview.cpp
new file mode 100644
index 0000000..bd0428e
--- /dev/null
+++ b/src/accountmigrationview.cpp
@@ -0,0 +1,282 @@
+/*
+ * 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 <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+// LRC
+#include <codecmodel.h>
+#include <account.h>
+#include <accountmodel.h>
+#include <profilemodel.h>
+#include <profile.h>
+#include <personmodel.h>
+#include <globalinstances.h>
+
+// Ring Client
+#include "avatarmanipulation.h"
+#include "accountmigrationview.h"
+#include "utils/models.h"
+#include "native/pixbufmanipulator.h"
+#include "video/video_widget.h"
+
+/* size of avatar */
+static constexpr int AVATAR_WIDTH = 100; /* px */
+static constexpr int AVATAR_HEIGHT = 100; /* px */
+
+struct _AccountMigrationView
+{
+ GtkBox parent;
+};
+
+struct _AccountMigrationViewClass
+{
+ GtkBoxClass parent_class;
+};
+
+typedef struct _AccountMigrationViewPrivate AccountMigrationViewPrivate;
+
+struct _AccountMigrationViewPrivate
+{
+ Account *account;
+ GtkWidget *stack_account_migration;
+ QMetaObject::Connection state_changed;
+ guint timeout_tag;
+
+ /* generating_account_view */
+ GtkWidget *migrating_account_view;
+
+ /* error_view */
+ GtkWidget *error_view;
+ GtkWidget *button_error_view_ok;
+
+ /* main_view */
+ GtkWidget *main_view;
+ GtkWidget *label_account_alias;
+ GtkWidget *label_account_id;
+ GtkWidget *image_avatar;
+ GtkWidget *label_password_error;
+ GtkWidget *entry_password;
+ GtkWidget *entry_password_confirm;
+ GtkWidget *button_migrate_account;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountMigrationView, account_migration_view, GTK_TYPE_BOX);
+
+#define ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationViewPrivate))
+
+/* signals */
+enum {
+ ACCOUNT_MIGRATION_COMPLETED,
+ ACCOUNT_MIGRATION_FAILED,
+ LAST_SIGNAL
+};
+
+static guint account_migration_view_signals[LAST_SIGNAL] = { 0 };
+
+static void
+account_migration_view_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(account_migration_view_parent_class)->dispose(object);
+}
+
+static void
+account_migration_view_init(AccountMigrationView *view)
+{
+ gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_migration_view_class_init(AccountMigrationViewClass *klass)
+{
+ G_OBJECT_CLASS(klass)->dispose = account_migration_view_dispose;
+
+ gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+ "/cx/ring/RingGnome/accountmigrationview.ui");
+
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, stack_account_migration);
+
+ /* error_view */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, error_view);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_error_view_ok);
+
+ /* migrating_account_view */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, migrating_account_view);
+
+ /* main_view */
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, main_view);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_alias);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_id);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, image_avatar);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_password_error);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password_confirm);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_migrate_account);
+
+ /* add signals */
+ account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED] = g_signal_new("account-migration-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_migration_view_signals[ACCOUNT_MIGRATION_FAILED] = g_signal_new("account-migration-failed",
+ 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 gboolean
+migration_timeout(AccountMigrationView *view)
+{
+ AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+ QObject::disconnect(priv->state_changed);
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->error_view);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+button_error_view_ok_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view)
+{
+ AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+ g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_FAILED], 0);
+}
+
+static void
+migrate_account_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view)
+{
+ AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+ gtk_widget_hide(priv->label_password_error);
+
+ /* 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;
+ }
+ else
+ {
+ gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->migrating_account_view);
+
+ priv->account->setArchivePassword(password);
+ priv->account->performAction(Account::EditAction::SAVE);
+
+ // Timeout in 30 seconds
+ priv->timeout_tag = g_timeout_add_full(G_PRIORITY_DEFAULT, 30000, (GSourceFunc)migration_timeout, view, NULL);
+
+ priv->state_changed = QObject::connect(
+ priv->account,
+ &Account::stateChanged,
+ [=] (Account::RegistrationState state) {
+ switch(state)
+ {
+ case Account::RegistrationState::READY:
+ case Account::RegistrationState::TRYING:
+ case Account::RegistrationState::UNREGISTERED:
+ {
+ // Make sure that the account is ready to be displayed.
+ priv->account << Account::EditAction::RELOAD;
+
+ AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+ QObject::disconnect(priv->state_changed); // only want to emit once
+ g_source_remove(priv->timeout_tag); // We didn't timeout
+ g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED], 0);
+ break;
+ }
+ case Account::RegistrationState::ERROR:
+ case Account::RegistrationState::INITIALIZING:
+ case Account::RegistrationState::COUNT__:
+ {
+ // Keep waiting...
+ break;
+ }
+ }
+ }
+ );
+
+ }
+}
+
+static void
+password_entry_changed(G_GNUC_UNUSED GtkEntry* entry, AccountMigrationView *view)
+{
+ AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_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_migrate_account, TRUE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(priv->button_migrate_account, FALSE);
+ }
+}
+
+static void
+build_migration_view(AccountMigrationView *view)
+{
+ g_return_if_fail(IS_ACCOUNT_MIGRATION_VIEW(view));
+ AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+
+ g_signal_connect(priv->button_migrate_account, "clicked", G_CALLBACK(migrate_account_clicked), view);
+ g_signal_connect(priv->button_error_view_ok, "clicked", G_CALLBACK(button_error_view_ok_clicked), view);
+ g_signal_connect(priv->entry_password, "changed", G_CALLBACK(password_entry_changed), view);
+ g_signal_connect(priv->entry_password_confirm, "changed", G_CALLBACK(password_entry_changed), view);
+
+ gtk_label_set_text(GTK_LABEL(priv->label_account_alias), priv->account->alias().toUtf8().constData());
+ gtk_label_set_text(GTK_LABEL(priv->label_account_id), priv->account->id().constData());
+
+ /* set the avatar picture */
+ auto photo = GlobalInstances::pixmapManipulator().contactPhoto(
+ ProfileModel::instance().selectedProfile()->person(),
+ QSize(AVATAR_WIDTH, AVATAR_HEIGHT),
+ false);
+ std::shared_ptr<GdkPixbuf> pixbuf_photo = photo.value<std::shared_ptr<GdkPixbuf>>();
+
+ if (photo.isValid()) {
+ gtk_image_set_from_pixbuf (GTK_IMAGE(priv->image_avatar), pixbuf_photo.get());
+ } else {
+ g_warning("invalid pixbuf");
+ }
+}
+
+GtkWidget *
+account_migration_view_new(Account* account)
+{
+ gpointer view = g_object_new(ACCOUNT_MIGRATION_VIEW_TYPE, NULL);
+ AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view);
+ priv->account = account;
+ build_migration_view(ACCOUNT_MIGRATION_VIEW(view));
+ return (GtkWidget *)view;
+}
diff --git a/src/accountmigrationview.h b/src/accountmigrationview.h
new file mode 100644
index 0000000..376bd4c
--- /dev/null
+++ b/src/accountmigrationview.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+class Account;
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_MIGRATION_VIEW_TYPE (account_migration_view_get_type ())
+#define ACCOUNT_MIGRATION_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationView))
+#define ACCOUNT_MIGRATION_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationViewClass))
+#define IS_ACCOUNT_MIGRATION_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_MIGRATION_VIEW_TYPE))
+#define IS_ACCOUNT_MIGRATION_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_MIGRATION_VIEW_TYPE))
+
+typedef struct _AccountMigrationView AccountMigrationView;
+typedef struct _AccountMigrationViewClass AccountMigrationViewClass;
+
+GType account_migration_view_get_type (void) G_GNUC_CONST;
+GtkWidget *account_migration_view_new (Account *account);
+
+G_END_DECLS
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:
diff --git a/src/ringmainwindow.cpp b/src/ringmainwindow.cpp
index 95d86d3..15cd5a3 100644
--- a/src/ringmainwindow.cpp
+++ b/src/ringmainwindow.cpp
@@ -64,13 +64,16 @@
#include "generalsettingsview.h"
#include "utils/accounts.h"
#include "ringwelcomeview.h"
+#include "accountmigrationview.h"
+#include "accountcreationwizard.h"
#include "recentcontactsview.h"
#include "chatview.h"
#include "avatarmanipulation.h"
#include "utils/files.h"
static constexpr const char* CALL_VIEW_NAME = "calls";
-static constexpr const char* CREATE_ACCOUNT_VIEW_NAME = "wizard";
+static constexpr const char* ACCOUNT_CREATION_WIZARD_VIEW_NAME = "account-creation-wizard";
+static constexpr const char* ACCOUNT_MIGRATION_VIEW_NAME = "account-migration-view";
static constexpr const char* GENERAL_SETTINGS_VIEW_NAME = "general";
static constexpr const char* AUDIO_SETTINGS_VIEW_NAME = "audio";
static constexpr const char* MEDIA_SETTINGS_VIEW_NAME = "media";
@@ -125,21 +128,6 @@
gboolean show_settings;
- /* account creation */
- GtkWidget *account_creation;
- GtkWidget *image_ring_logo;
- GtkWidget *vbox_account_creation_entry;
- GtkWidget *entry_alias;
- GtkWidget *label_default_name;
- GtkWidget *label_paceholder;
- GtkWidget *label_generating_account;
- GtkWidget *spinner_generating_account;
- GtkWidget *button_account_creation_next;
- GtkWidget *box_avatarselection;
- GtkWidget* avatar_manipulation;
-
- QMetaObject::Connection hash_updated;
-
/* allocd qmodels */
NumberCompletionModel *q_completion_model;
@@ -591,153 +579,56 @@
}
}
-static gboolean
-create_ring_account(RingMainWindow *win)
-{
- g_return_val_if_fail(IS_RING_MAIN_WINDOW(win), G_SOURCE_REMOVE);
- RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
-
- /* create account and set UPnP enabled, as its not by default in the daemon */
- const gchar *alias = gtk_entry_get_text(GTK_ENTRY(priv->entry_alias));
- Account *account = nullptr;
-
- /* get profile (if so) */
- auto profile = ProfileModel::instance().selectedProfile();
-
- if (profile) {
- if (alias && strlen(alias) > 0) {
- account = AccountModel::instance().add(alias, Account::Protocol::RING);
- 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);
- profile->person()->setFormattedName(unknown_alias);
- }
- }
-
- account->setDisplayName(alias); // set the display name to the same as the alias
- account->setUpnpEnabled(TRUE);
-
- /* wait for hash to be generated to show the main view */
- priv->hash_updated = QObject::connect(
- account,
- &Account::changed,
- [=] (Account *a) {
- QString hash = a->username();
- if (!hash.isEmpty()) {
- /* show the call view */
- gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
- gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
-
- /* show the settings button*/
- gtk_widget_show(priv->ring_settings);
-
- //once the account is created we don't want this lambda to be called again
- QObject::disconnect(priv->hash_updated);
- }
- }
- );
-
- account->performAction(Account::EditAction::SAVE);
- profile->save();
-
- return G_SOURCE_REMOVE;
-}
-
static void
-alias_entry_changed(GtkEditable *entry, RingMainWindow *win)
+on_account_creation_completed(RingMainWindow *win)
{
g_return_if_fail(IS_RING_MAIN_WINDOW(win));
RingMainWindowPrivate *priv = RING_MAIN_WINDOW_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);
+ gtk_stack_set_transition_type(GTK_STACK(priv->stack_main_view), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+ gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CALL_VIEW_NAME);
+
+ /* destroy the wizard */
+ GtkWidget* account_creation_wizard = gtk_stack_get_child_by_name(
+ GTK_STACK(priv->stack_main_view),
+ ACCOUNT_CREATION_WIZARD_VIEW_NAME
+ );
+ if (account_creation_wizard)
+ {
+ gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), account_creation_wizard);
+ gtk_widget_destroy(account_creation_wizard);
}
+
+ /* show the settings button*/
+ gtk_widget_show(priv->ring_settings);
}
static void
-account_creation_next_clicked(G_GNUC_UNUSED GtkButton *button, RingMainWindow *win)
+show_account_creation_wizard(RingMainWindow *win)
{
RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
- /* show/hide relevant widgets */
- gtk_widget_hide(priv->vbox_account_creation_entry);
- gtk_widget_hide(priv->button_account_creation_next);
- gtk_widget_show(priv->label_generating_account);
- gtk_widget_show(priv->spinner_generating_account);
+ auto account_creation_wizard = gtk_stack_get_child_by_name(
+ GTK_STACK(priv->stack_main_view),
+ ACCOUNT_CREATION_WIZARD_VIEW_NAME
+ );
- /* 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 */
- gtk_container_remove(GTK_CONTAINER(priv->box_avatarselection), priv->avatar_manipulation);
- priv->avatar_manipulation = nullptr;
+ if (!account_creation_wizard)
+ {
+ account_creation_wizard = account_creation_wizard_new(false);
+ g_signal_connect_swapped(account_creation_wizard, "account-creation-completed", G_CALLBACK(on_account_creation_completed), 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_ring_account, win, NULL);
-}
-
-static void
-entry_alias_activated(GtkEntry *entry, RingMainWindow *win)
-{
- RingMainWindowPrivate *priv = RING_MAIN_WINDOW_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_account_creation(RingMainWindow *win)
-{
- RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
-
- gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
- priv->account_creation,
- CREATE_ACCOUNT_VIEW_NAME);
+ gtk_stack_add_named(GTK_STACK(priv->stack_main_view),
+ account_creation_wizard,
+ ACCOUNT_CREATION_WIZARD_VIEW_NAME);
+ }
/* hide settings button until account creation is complete */
gtk_widget_hide(priv->ring_settings);
- /* 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->image_ring_logo), logo_ring);
+ gtk_widget_show(account_creation_wizard);
- /* 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);
-
- /* avatar manipulation widget */
- priv->avatar_manipulation = avatar_manipulation_new_from_wizard();
- gtk_box_pack_start(GTK_BOX(priv->box_avatarselection), priv->avatar_manipulation, true, true, 0);
-
- /* connect signals */
- g_signal_connect(priv->entry_alias, "changed", G_CALLBACK(alias_entry_changed), win);
- g_signal_connect(priv->button_account_creation_next, "clicked", G_CALLBACK(account_creation_next_clicked), win);
- g_signal_connect(priv->entry_alias, "activate", G_CALLBACK(entry_alias_activated), win);
-
- gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), CREATE_ACCOUNT_VIEW_NAME);
+ gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_CREATION_WIZARD_VIEW_NAME);
}
static void
@@ -1121,6 +1012,46 @@
}
static void
+handle_account_migrations(RingMainWindow *win)
+{
+ RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
+
+ /* If there is an existing migration view, remove it */
+ GtkWidget* old_view = gtk_stack_get_child_by_name(GTK_STACK(priv->stack_main_view), ACCOUNT_MIGRATION_VIEW_NAME);
+ if (old_view)
+ {
+ gtk_container_remove(GTK_CONTAINER(priv->stack_main_view), old_view);
+ }
+
+ QList<Account*> accounts = AccountModel::instance().accountsToMigrate();
+ if (!accounts.isEmpty())
+ {
+ Account* account = accounts.first();
+ g_debug("Migrating account: %s", account->id().toStdString().c_str());
+
+ GtkWidget* account_migration_view = account_migration_view_new(account);
+ g_signal_connect_swapped(account_migration_view, "account-migration-completed", G_CALLBACK(handle_account_migrations), win);
+ g_signal_connect_swapped(account_migration_view, "account-migration-failed", G_CALLBACK(handle_account_migrations), win);
+
+ gtk_widget_hide(priv->ring_settings);
+ gtk_widget_show(account_migration_view);
+ gtk_stack_add_named(
+ GTK_STACK(priv->stack_main_view),
+ account_migration_view,
+ ACCOUNT_MIGRATION_VIEW_NAME
+ );
+ gtk_stack_set_visible_child_name(
+ GTK_STACK(priv->stack_main_view),
+ ACCOUNT_MIGRATION_VIEW_NAME
+ );
+ }
+ else
+ {
+ gtk_widget_show(priv->ring_settings);
+ }
+}
+
+static void
ring_main_window_init(RingMainWindow *win)
{
RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(win);
@@ -1177,7 +1108,7 @@
gtk_stack_set_visible_child(GTK_STACK(priv->stack_main_view), priv->vbox_call_view);
} else {
/* user has to create the ring account */
- show_account_creation(win);
+ show_account_creation_wizard(win);
}
/* init the settings views */
@@ -1322,6 +1253,8 @@
/* set the search entry placeholder text */
gtk_entry_set_placeholder_text(GTK_ENTRY(priv->search_entry),
C_("Please try to make the translation 50 chars or less so that it fits into the layout", "Search contacts or enter number"));
+
+ handle_account_migrations(win);
}
static void
@@ -1374,18 +1307,6 @@
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_general_settings);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_media_settings);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, radiobutton_account_settings);
-
- /* account creation */
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, account_creation);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, image_ring_logo);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, vbox_account_creation_entry);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, entry_alias);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_default_name);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_paceholder);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, label_generating_account);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, spinner_generating_account);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_account_creation_next);
- gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, box_avatarselection);
}
GtkWidget *