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>