accountview: add account import/export

Allows user to export a selected account and import
from an archive. For now the interface only allows the
selection of one account for export. Multi-account selection
can be added later.

Change-Id: I1ac46c695b335cbdc5b2cee2ea6abee618ba9c3b
Tuleap: #617
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0622c8e..1549826 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -310,6 +310,8 @@
    src/chatview.cpp
    src/avatarmanipulation.h
    src/avatarmanipulation.cpp
+   src/accountimportexportview.h
+   src/accountimportexportview.cpp
 )
 
 # compile glib resource files to c code
diff --git a/src/accountimportexportview.cpp b/src/accountimportexportview.cpp
new file mode 100644
index 0000000..2523e53
--- /dev/null
+++ b/src/accountimportexportview.cpp
@@ -0,0 +1,411 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include "accountimportexportview.h"
+
+#include <gtk/gtk.h>
+#include <account.h>
+#include <accountmodel.h>
+
+#include <glib/gi18n.h>
+
+struct _AccountImportExportView
+{
+    GtkBox parent;
+};
+
+struct _AccountImportExportViewClass
+{
+    GtkBoxClass parent_class;
+};
+
+typedef struct _AccountImportExportViewPrivate AccountImportExportViewPrivate;
+
+struct _AccountImportExportViewPrivate
+{
+    Account   *account;
+
+    GtkWidget *label_export;
+    GtkWidget *label_import;
+    GtkWidget *hbox_export_location;
+    GtkWidget *label_export_location;
+    GtkWidget *button_export_location;
+    GtkWidget *filechooserbutton_import;
+    GtkWidget *button_export;
+    GtkWidget *button_import;
+    GtkWidget *button_cancel;
+    GtkWidget *entry_password;
+    GtkWidget *label_error;
+
+    GList *export_accounts_list;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountImportExportView, account_importexport_view, GTK_TYPE_BOX);
+
+#define ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_IMPORTEXPORT_VIEW_TYPE, AccountImportExportViewPrivate))
+
+/* signals */
+enum {
+    IMPORT_EXPORT_CANCELED,
+    IMPORT_EXPORT_COMPLETED,
+    LAST_SIGNAL
+};
+
+static guint account_importexport_view_signals[LAST_SIGNAL] = { 0 };
+
+static void
+account_importexport_view_dispose(GObject *object)
+{
+    G_OBJECT_CLASS(account_importexport_view_parent_class)->dispose(object);
+}
+
+static void
+account_importexport_view_finalize(GObject *object)
+{
+    AccountImportExportView *view = ACCOUNT_IMPORTEXPORT_VIEW(object);
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(view);
+
+    if (priv->export_accounts_list)
+        g_list_free(priv->export_accounts_list);
+
+    G_OBJECT_CLASS(account_importexport_view_parent_class)->finalize(object);
+}
+
+static void
+choose_export_location(AccountImportExportView *self)
+{
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    // clear any existing error
+    gtk_label_set_text(GTK_LABEL(priv->label_error), "");
+
+    // create filechooser dialog and get export location
+    auto dialog = gtk_file_chooser_dialog_new(_("Select account export location"),
+                                              GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(self))),
+                                              GTK_FILE_CHOOSER_ACTION_SAVE,
+                                              _("Cancel"),
+                                              GTK_RESPONSE_CANCEL,
+                                              _("Select"),
+                                              GTK_RESPONSE_ACCEPT,
+                                              NULL);
+
+    gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
+    gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog), TRUE);
+
+    if (!priv->export_accounts_list->next) {
+        auto name = g_strconcat(static_cast<Account *>(priv->export_accounts_list->data)->alias().toUtf8().constData(), ".ring", NULL);
+        gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), name);
+        g_free(name);
+    } else {
+        // TODO: handle multiple account export
+    }
+
+    /* start the file chooser */
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+        if (auto filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog))) {
+            gtk_label_set_text(GTK_LABEL(priv->label_export_location), filename);
+
+            // if accounts and password are set then we're ready for export
+            auto password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+            if (priv->export_accounts_list && priv->export_accounts_list->data && strlen(password) > 0) {
+                gtk_widget_set_sensitive(priv->button_export, TRUE);
+            }
+            g_free (filename);
+        }
+    }
+
+    gtk_widget_destroy (dialog);
+}
+
+
+static void
+import_file_set(AccountImportExportView *self)
+{
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    // clear any existing error
+    gtk_label_set_text(GTK_LABEL(priv->label_error), "");
+
+    // if password is set then we're ready for import
+    auto password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    if (strlen(password) > 0) {
+        gtk_widget_set_sensitive(priv->button_import, TRUE);
+    }
+}
+
+static void
+password_changed(AccountImportExportView *self)
+{
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    // clear any existing error
+    gtk_label_set_text(GTK_LABEL(priv->label_error), "");
+
+    // if the password and other requirements are met then enable import/export
+    auto password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+    if (strlen(password) > 0) {
+
+        // import
+        if (auto filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->filechooserbutton_import))) {
+            gtk_widget_set_sensitive(priv->button_import, TRUE);
+            g_free(filename);
+        }
+
+        // export
+        const auto filename = gtk_label_get_text(GTK_LABEL(priv->label_export_location));
+        if (strlen(filename) > 0) {
+            if (priv->export_accounts_list && priv->export_accounts_list->data) {
+                gtk_widget_set_sensitive(priv->button_export, TRUE);
+            }
+        }
+
+    } else {
+        gtk_widget_set_sensitive(priv->button_export, FALSE);
+        gtk_widget_set_sensitive(priv->button_import, FALSE);
+    }
+}
+
+static void
+cancel_clicked(AccountImportExportView *self)
+{
+    g_signal_emit(G_OBJECT(self), account_importexport_view_signals[IMPORT_EXPORT_CANCELED], 0);
+}
+
+static void
+import_account(AccountImportExportView *self)
+{
+    g_return_if_fail(IS_ACCOUNT_IMPORTEXPORT_VIEW(self));
+
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    // clear any existing error
+    gtk_label_set_text(GTK_LABEL(priv->label_error), "");
+
+    if (auto filepath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(priv->filechooserbutton_import))) {
+        const auto password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+        if (strlen(password) > 0) {
+            auto ret = AccountModel::instance().importAccounts(filepath, password);
+            switch(ret) {
+                case 0:
+                    // done
+                    g_signal_emit(G_OBJECT(self), account_importexport_view_signals[IMPORT_EXPORT_COMPLETED], 0);
+                break;
+                default:
+                    //failed
+                    gtk_label_set_text(GTK_LABEL(priv->label_error), _("Error importing account(s)"));
+                    g_warning("failed to import account(s), err: %d", ret);
+                break;
+            }
+        } else {
+            g_warning("no password set for account import");
+        }
+        g_free(filepath);
+    } else {
+        g_warning("no file selected for account import");
+    }
+}
+
+static void
+export_account(AccountImportExportView *self)
+{
+    g_return_if_fail(IS_ACCOUNT_IMPORTEXPORT_VIEW(self));
+
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    // clear any existing error
+    gtk_label_set_text(GTK_LABEL(priv->label_error), "");
+
+    // check that we have some accounts to export
+    if ( !(priv->export_accounts_list && priv->export_accounts_list->data)) {
+        g_warning("no accounts are selected for export");
+        return;
+    }
+
+    const auto filepath = gtk_label_get_text(GTK_LABEL(priv->label_export_location));
+    // validate filepath
+    if (strlen(filepath)) {
+        const auto password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password));
+        if (strlen(password) > 0) {
+
+            // get account id strings
+            auto account_ids = QStringList();
+            auto list = priv->export_accounts_list;
+            while (list != nullptr) {
+                auto account = static_cast<Account *>(list->data);
+                account_ids << account->id();
+                list = g_list_next(list);
+            }
+
+            auto ret = AccountModel::instance().exportAccounts(account_ids, filepath, password);
+            switch (ret) {
+                case 0:
+                    // done
+                    g_signal_emit(G_OBJECT(self), account_importexport_view_signals[IMPORT_EXPORT_COMPLETED], 0);
+                break;
+                default:
+                    //failed
+                    gtk_label_set_text(GTK_LABEL(priv->label_error), _("Error exporting account(s)"));
+                    g_warning("failed to export account(s), err: %d", ret);
+                break;
+            }
+        } else {
+            g_warning("no password set for account export");
+        }
+    } else {
+        g_warning("no file selected for account export");
+    }
+}
+
+static void
+account_importexport_view_init(AccountImportExportView *self)
+{
+    gtk_widget_init_template(GTK_WIDGET(self));
+
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    g_signal_connect_swapped(priv->button_export_location, "clicked", G_CALLBACK(choose_export_location), self);
+    g_signal_connect_swapped(priv->filechooserbutton_import, "file-set", G_CALLBACK(import_file_set), self);
+    g_signal_connect_swapped(priv->entry_password, "changed", G_CALLBACK(password_changed), self);
+    g_signal_connect_swapped(priv->button_cancel, "clicked", G_CALLBACK(cancel_clicked), self);
+    g_signal_connect_swapped(priv->button_import, "clicked", G_CALLBACK(import_account), self);
+    g_signal_connect_swapped(priv->button_export, "clicked", G_CALLBACK(export_account), self);
+}
+
+static void
+account_importexport_view_class_init(AccountImportExportViewClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->dispose = account_importexport_view_dispose;
+    gobject_class->finalize = account_importexport_view_finalize;
+
+    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+                                                "/cx/ring/RingGnome/accountimportexportview.ui");
+
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, label_export);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, label_import);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, hbox_export_location);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, label_export_location);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, button_export_location);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, filechooserbutton_import);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, button_export);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, button_import);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, button_cancel);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, entry_password);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountImportExportView, label_error);
+
+    /* add signals */
+    account_importexport_view_signals[IMPORT_EXPORT_CANCELED] = g_signal_new("import-export-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);
+
+    account_importexport_view_signals[IMPORT_EXPORT_COMPLETED] = g_signal_new("import-export-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);
+}
+
+static void
+build_import_view(AccountImportExportView *self)
+{
+    g_return_if_fail(self);
+
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    gtk_widget_hide(priv->label_export);
+    gtk_widget_hide(priv->hbox_export_location);
+    gtk_widget_hide(priv->button_export);
+
+    gtk_widget_show(priv->label_import);
+    gtk_widget_show(priv->filechooserbutton_import);
+    gtk_widget_show(priv->button_import);
+}
+
+static void
+build_export_view(AccountImportExportView *self)
+{
+    g_return_if_fail(self);
+
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    gtk_widget_show(priv->label_export);
+    gtk_widget_show(priv->hbox_export_location);
+    gtk_widget_show(priv->button_export);
+
+    gtk_widget_hide(priv->label_import);
+    gtk_widget_hide(priv->filechooserbutton_import);
+    gtk_widget_hide(priv->button_import);
+}
+
+GtkWidget *
+account_import_view_new()
+{
+    gpointer view = g_object_new(ACCOUNT_IMPORTEXPORT_VIEW_TYPE, NULL);
+
+    build_import_view(ACCOUNT_IMPORTEXPORT_VIEW(view));
+
+    return (GtkWidget *)view;
+}
+
+GtkWidget *
+account_export_view_new()
+{
+    gpointer view = g_object_new(ACCOUNT_IMPORTEXPORT_VIEW_TYPE, NULL);
+
+    build_export_view(ACCOUNT_IMPORTEXPORT_VIEW(view));
+
+    return (GtkWidget *)view;
+}
+
+void
+account_export_view_set_accounts(AccountImportExportView *self, GList *accounts)
+{
+    g_return_if_fail(self);
+    AccountImportExportViewPrivate *priv = ACCOUNT_IMPORTEXPORT_VIEW_GET_PRIVATE(self);
+
+    // replace current list
+    if (priv->export_accounts_list) {
+        g_list_free(priv->export_accounts_list);
+        priv->export_accounts_list = nullptr;
+    }
+
+    // make sure the new list isn't empty
+    if (accounts && accounts->data) {
+        priv->export_accounts_list = g_list_copy(accounts);
+
+        if (!accounts->next) {
+            auto location = g_strconcat(g_get_home_dir(), "/", static_cast<Account *>(priv->export_accounts_list->data)->alias().toUtf8().constData(), ".ring", NULL);
+            gtk_label_set_text(GTK_LABEL(priv->label_export_location), location);
+            g_free(location);
+        } else {
+            // TODO: handle multiple account export
+        }
+    } else {
+        // no accounts are selected... this case should not normally be ever displayed
+    }
+}
diff --git a/src/accountimportexportview.h b/src/accountimportexportview.h
new file mode 100644
index 0000000..f2b9324
--- /dev/null
+++ b/src/accountimportexportview.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_IMPORTEXPORT_VIEW_TYPE            (account_importexport_view_get_type ())
+#define ACCOUNT_IMPORTEXPORT_VIEW(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_IMPORTEXPORT_VIEW_TYPE, AccountImportExportView))
+#define ACCOUNT_IMPORTEXPORT_VIEW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_IMPORTEXPORT_VIEW_TYPE, AccountImportExportViewClass))
+#define IS_ACCOUNT_IMPORTEXPORT_VIEW(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_IMPORTEXPORT_VIEW_TYPE))
+#define IS_ACCOUNT_IMPORTEXPORT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_IMPORTEXPORT_VIEW_TYPE))
+
+typedef struct _AccountImportExportView      AccountImportExportView;
+typedef struct _AccountImportExportViewClass AccountImportExportViewClass;
+
+GType      account_importexport_view_get_type      (void) G_GNUC_CONST;
+GtkWidget *account_import_view_new                 (void);
+GtkWidget *account_export_view_new                 (void);
+void       account_export_view_set_accounts        (AccountImportExportView* self, GList *accounts);
+
+G_END_DECLS
diff --git a/src/accountview.cpp b/src/accountview.cpp
index dd57868..30ef947 100644
--- a/src/accountview.cpp
+++ b/src/accountview.cpp
@@ -36,6 +36,7 @@
 #include "dialogs.h"
 #include <glib/gprintf.h>
 #include "utils/models.h"
+#include "accountimportexportview.h"
 
 struct _AccountView
 {
@@ -52,11 +53,14 @@
 struct _AccountViewPrivate
 {
     GtkWidget *treeview_account_list;
+    GtkWidget *account_import_view;
+    GtkWidget *account_export_view;
     GtkWidget *stack_account;
-    GtkWidget *current_account_notebook;
     GtkWidget *button_remove_account;
     GtkWidget *button_add_account;
     GtkWidget *combobox_account_type;
+    GtkWidget *button_import_account;
+    GtkWidget *button_export_account;
 
     gint current_page; /* keeps track of current notebook page displayed */
 
@@ -109,19 +113,24 @@
 }
 
 static void
-account_selection_changed(GtkTreeSelection *selection, AccountView *view)
+tab_selection_changed(G_GNUC_UNUSED GtkNotebook *notebook,
+                      G_GNUC_UNUSED GtkWidget   *page,
+                                    guint        page_num,
+                                    AccountView *self)
 {
-    g_return_if_fail(IS_ACCOUNT_VIEW(view));
-    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+    g_return_if_fail(IS_ACCOUNT_VIEW(self));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
 
-    GtkWidget *old_account_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+    priv->current_page = page_num;
+}
 
-    /* keep track of the last tab displayed */
-    if (priv->current_account_notebook)
-        priv->current_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->current_account_notebook));
+static void
+account_selection_changed(GtkTreeSelection *selection, AccountView *self)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(self));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
 
-    if (priv->current_page < 0)
-        priv->current_page = 0;
+    auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
 
     QModelIndex account_idx = get_index_from_selection(selection);
     if (!account_idx.isValid()) {
@@ -130,7 +139,10 @@
         gtk_widget_show(empty_box);
         gtk_stack_add_named(GTK_STACK(priv->stack_account), empty_box, "placeholder");
         gtk_stack_set_visible_child(GTK_STACK(priv->stack_account), empty_box);
-        priv->current_account_notebook = NULL;
+
+        /* cannot delete nor export accounts */
+        gtk_widget_set_sensitive(priv->button_remove_account, FALSE);
+        gtk_widget_set_sensitive(priv->button_export_account, FALSE);
     } else {
         Account *account = AccountModel::instance().getAccountByModelIndex(account_idx);
 
@@ -138,47 +150,53 @@
         GtkWidget *hbox_account = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
 
         /* create account notebook */
-        priv->current_account_notebook = gtk_notebook_new();
-        gtk_notebook_set_scrollable(GTK_NOTEBOOK(priv->current_account_notebook), TRUE);
-        gtk_notebook_set_show_border(GTK_NOTEBOOK(priv->current_account_notebook), FALSE);
-        gtk_box_pack_start(GTK_BOX(hbox_account), priv->current_account_notebook, TRUE, TRUE, 0);
+        auto account_notebook = gtk_notebook_new();
+        gtk_notebook_set_scrollable(GTK_NOTEBOOK(account_notebook), TRUE);
+        gtk_notebook_set_show_border(GTK_NOTEBOOK(account_notebook), FALSE);
+        gtk_box_pack_start(GTK_BOX(hbox_account), account_notebook, TRUE, TRUE, 0);
 
         /* customize account view based on account */
         auto general_tab = create_scrolled_account_view(account_general_tab_new(account));
-        gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+        gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
                                  general_tab,
                                  gtk_label_new(C_("Account settings", "General")));
         auto audio_tab = create_scrolled_account_view(account_audio_tab_new(account));
-        gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+        gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
                                  audio_tab,
                                  gtk_label_new(C_("Account settings", "Audio")));
         auto video_tab = create_scrolled_account_view(account_video_tab_new(account));
-        gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+        gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
                                  video_tab,
                                  gtk_label_new(C_("Account settings", "Video")));
         auto advanced_tab = create_scrolled_account_view(account_advanced_tab_new(account));
-        gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+        gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
                                  advanced_tab,
                                  gtk_label_new(C_("Account settings", "Advanced")));
         auto security_tab = create_scrolled_account_view(account_security_tab_new(account));
-        gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+        gtk_notebook_append_page(GTK_NOTEBOOK(account_notebook),
                                  security_tab,
                                  gtk_label_new(C_("Account settings", "Security")));
 
         gtk_widget_show_all(hbox_account);
         /* set the tab displayed to the same as the prev account selected */
-        gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->current_account_notebook), priv->current_page);
+        gtk_notebook_set_current_page(GTK_NOTEBOOK(account_notebook), priv->current_page);
+        /* now connect to the tab changed signal */
+        g_signal_connect(account_notebook, "switch-page", G_CALLBACK(tab_selection_changed), self);
 
         /* set the new account view as visible */
         char *account_view_name = g_strdup_printf("%p_account", account);
         gtk_stack_add_named(GTK_STACK(priv->stack_account), hbox_account, account_view_name);
         gtk_stack_set_visible_child(GTK_STACK(priv->stack_account), hbox_account);
         g_free(account_view_name);
+
+        /* can delete and export accounts */
+        gtk_widget_set_sensitive(priv->button_remove_account, TRUE);
+        gtk_widget_set_sensitive(priv->button_export_account, TRUE);
     }
 
-    /* remove the old account view */
-    if (old_account_view)
-        gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_account_view);
+    /* remove the old view */
+    if (old_view)
+        gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
 }
 
 static void
@@ -366,6 +384,69 @@
 }
 
 static void
+close_import_export_view(AccountView *self)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(self));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
+
+    auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
+    account_selection_changed(selection, self);
+}
+
+static void
+import_account(AccountView* self)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(self));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
+
+    auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+
+    auto import_account = account_import_view_new();
+    g_signal_connect_swapped(import_account, "import-export-canceled", G_CALLBACK(close_import_export_view), self);
+    g_signal_connect_swapped(import_account, "import-export-completed", G_CALLBACK(close_import_export_view), self);
+    auto scrolled_view = create_scrolled_account_view(import_account);
+    gtk_widget_show_all(scrolled_view);
+    gtk_stack_add_named(GTK_STACK(priv->stack_account), scrolled_view, "import_account");
+    gtk_stack_set_visible_child(GTK_STACK(priv->stack_account), scrolled_view);
+
+    /* remove the old view */
+    if (old_view)
+        gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+}
+
+static void
+export_account(AccountView* self)
+{
+    g_return_if_fail(IS_ACCOUNT_VIEW(self));
+    AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(self);
+
+    auto selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
+    auto idx = get_index_from_selection(selection);
+    if (idx.isValid()) {
+        auto old_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+
+        auto account = AccountModel::instance().getAccountByModelIndex(idx);
+        auto export_account = account_export_view_new();
+        g_signal_connect_swapped(export_account, "import-export-canceled", G_CALLBACK(close_import_export_view), self);
+        g_signal_connect_swapped(export_account, "import-export-completed", G_CALLBACK(close_import_export_view), self);
+        GList *account_list = nullptr;
+        account_list = g_list_append(account_list, account);
+        account_export_view_set_accounts(ACCOUNT_IMPORTEXPORT_VIEW(export_account), account_list);
+        g_list_free(account_list);
+        auto scrolled_view = create_scrolled_account_view(export_account);
+        gtk_widget_show_all(scrolled_view);
+        gtk_stack_add_named(GTK_STACK(priv->stack_account), scrolled_view, "export_account");
+        gtk_stack_set_visible_child(GTK_STACK(priv->stack_account), scrolled_view);
+
+        /* remove the old view */
+        if (old_view)
+            gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_view);
+
+    }
+
+}
+
+static void
 account_view_init(AccountView *view)
 {
     gtk_widget_init_template(GTK_WIDGET(view));
@@ -474,6 +555,10 @@
     /* connect signals to add/remove accounts */
     g_signal_connect(priv->button_remove_account, "clicked", G_CALLBACK(remove_account), view);
     g_signal_connect(priv->button_add_account, "clicked", G_CALLBACK(add_account), view);
+
+    /* signals to import/export acounts */
+    g_signal_connect_swapped(priv->button_import_account, "clicked", G_CALLBACK(import_account), view);
+    g_signal_connect_swapped(priv->button_export_account, "clicked", G_CALLBACK(export_account), view);
 }
 
 static void
@@ -490,6 +575,8 @@
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, button_remove_account);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, button_add_account);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, combobox_account_type);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, button_import_account);
+    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, button_export_account);
 }
 
 GtkWidget *
diff --git a/ui/accountimportexportview.ui b/ui/accountimportexportview.ui
new file mode 100644
index 0000000..1a7f4db
--- /dev/null
+++ b/ui/accountimportexportview.ui
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.10"/>
+  <template class="AccountImportExportView" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="orientation">vertical</property>
+    <property name="spacing">10</property>
+    <property name="border-width">10</property>
+
+    <!-- export or import label, depending on whats  being done -->
+    <child>
+      <object class="GtkLabel" id="label_export">
+        <property name="visible">False</property>
+        <property name="label" translatable="yes">Export selected account</property>
+        <property name="halign">center</property>
+        <property name="valign">start</property>
+        <property name="no-show-all">True</property>
+        <attributes>
+          <attribute name="weight" value="bold"/>
+        </attributes>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="label_import">
+        <property name="visible">False</property>
+        <property name="label" translatable="yes">Import accounts</property>
+        <property name="halign">center</property>
+        <property name="valign">start</property>
+        <property name="no-show-all">True</property>
+        <attributes>
+          <attribute name="weight" value="bold"/>
+        </attributes>
+      </object>
+    </child>
+    <!-- end import/export label -->
+
+    <child>
+      <object class="GtkGrid" id="grid_ipmort_export">
+        <property name="visible">True</property>
+        <property name="column-spacing">10</property>
+        <property name="row-spacing">10</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+
+        <!-- location selection row -->
+        <child>
+          <object class="GtkLabel" id="label_location">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Location:</property>
+            <property name="halign">end</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+
+        <!-- filechooser for export -->
+        <child>
+          <object class="GtkBox" id="hbox_export_location">
+            <property name="visible">False</property>
+            <property name="orientation">horizontal</property>
+            <property name="spacing">5</property>
+            <property name="no-show-all">True</property>
+            <child>
+              <object class="GtkLabel" id="label_export_location">
+                <property name="visible">True</property>
+                <property name="ellipsize">start</property>
+                <property name="selectable">True</property>
+                <property name="label" translatable="yes">choose location</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="button_export_location">
+                <property name="visible">True</property>
+                <property name="tooltip-text" translatable="yes">Choose export location</property>
+                <property name="image">image_choose_file</property>
+                <property name="halign">end</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <!-- end filechooser for export -->
+
+        <!-- filechooser for import -->
+        <child>
+          <object class="GtkFileChooserButton" id="filechooserbutton_import">
+            <property name="visible">False</property>
+            <property name="create_folders">False</property>
+            <property name="action">open</property>
+            <property name="tooltip-text" translatable="yes">Choose archive location</property>
+            <property name="no-show-all">True</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <!-- end filechooser for import -->
+
+        <!-- end location selection row -->
+
+        <!-- password row -->
+        <child>
+          <object class="GtkLabel" id="label_password">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Password (required):</property>
+            <property name="halign">end</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+
+        <child>
+          <object class="GtkEntry" id="entry_password">
+            <property name="visible">True</property>
+            <property name="visibility">False</property>
+            <property name="primary_icon_stock">gtk-dialog-authentication</property>
+            <property name="input_purpose">password</property>
+            <property name="tooltip-text" translatable="yes">Enter archive password</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </child>
+        <!-- end password row -->
+
+        <!-- error message label -->
+        <child>
+          <object class="GtkLabel" id="label_error">
+            <property name="visible">True</property>
+            <attributes>
+              <attribute name="foreground" value="red"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">3</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <!-- end error message label -->
+
+        <child>
+          <object class="GtkButtonBox" id="buttonbox_confirm">
+            <property name="visible">True</property>
+            <property name="layout-style">spread</property>
+            <!-- <property name="valign">end</property> -->
+            <property name="spacing">10</property>
+
+            <child>
+              <object class="GtkButton" id="button_cancel">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Cancel</property>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkButton" id="button_export">
+                <property name="visible">False</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Export</property>
+                <property name="no-show-all">True</property>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+            </child>
+
+            <child>
+              <object class="GtkButton" id="button_import">
+                <property name="visible">False</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Import</property>
+                <property name="no-show-all">True</property>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+            </child>
+
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">4</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+
+      </object>
+      <packing>
+        <property name="fill">True</property>
+        <property name="expand">True</property>
+      </packing>
+    </child>
+
+  </template>
+
+  <object class="GtkImage" id="image_choose_file">
+    <property name="visible">True</property>
+    <property name="icon-name">list-add-symbolic</property>
+  </object>
+
+</interface>
diff --git a/ui/accountview.ui b/ui/accountview.ui
index d2641c4..f3c15c9 100644
--- a/ui/accountview.ui
+++ b/ui/accountview.ui
@@ -30,7 +30,7 @@
         <child>
           <object class="GtkBox" id="hbox_addremove_account">
             <property name="visible">True</property>
-s            <property name="orientation">horizontal</property>
+            <property name="orientation">horizontal</property>
             <property name="spacing">5</property>
             <property name="halign">GTK_ALIGN_CENTER</property>
             <!-- remove account -->
@@ -38,6 +38,7 @@
               <object class="GtkButton" id="button_remove_account">
                 <property name="visible">True</property>
                 <property name="image">image_remove</property>
+                <property name="sensitive">False</property>
                 <property name="tooltip-text" translatable="yes">Remove selected account</property>
                 <child internal-child="accessible">
                   <object class="AtkObject" id="button_remove_account-atkobject">
@@ -85,6 +86,31 @@
           </object>
         </child>
         <!-- end add remove accounts -->
+
+        <!-- import/export account -->
+        <child>
+          <object class="GtkButtonBox" id="buttonbox_import_export">
+            <property name="visible">True</property>
+            <property name="orientation">horizontal</property>
+            <property name="layout-style">spread</property>
+            <property name="margin-top">5</property>
+            <child>
+              <object class="GtkButton" id="button_import_account">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Import</property>
+                <property name="tooltip-text" translatable="yes">Import archived account(s)</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="button_export_account">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Export</property>
+                <property name="sensitive">False</property>
+                <property name="tooltip-text" translatable="yes">Export selected account(s)</property>
+              </object>
+            </child>
+          </object>
+        </child>
       </object>
       <!-- <packing>
       </packing> -->
diff --git a/ui/ui.gresource.xml b/ui/ui.gresource.xml
index 59fb41a..9842397 100644
--- a/ui/ui.gresource.xml
+++ b/ui/ui.gresource.xml
@@ -18,5 +18,6 @@
     <file preprocess="xml-stripblanks">chatview.ui</file>
     <file preprocess="xml-stripblanks">avatarmanipulation.ui</file>
     <file preprocess="xml-stripblanks">ringiconmenu.ui</file>
+    <file preprocess="xml-stripblanks">accountimportexportview.ui</file>
   </gresource>
 </gresources>