aviau | 6aeb485 | 2016-08-18 16:01:09 -0400 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2016 Savoir-faire Linux Inc. |
| 3 | * Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com> |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License as published by |
| 7 | * the Free Software Foundation; either version 3 of the License, or |
| 8 | * (at your option) any later version. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License |
| 16 | * along with this program; if not, write to the Free Software |
| 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | */ |
| 19 | |
| 20 | |
| 21 | // GTK+ related |
| 22 | #include <gtk/gtk.h> |
| 23 | #include <glib/gi18n.h> |
| 24 | |
| 25 | // LRC |
| 26 | #include <codecmodel.h> |
| 27 | #include <account.h> |
| 28 | #include <accountmodel.h> |
| 29 | #include <profilemodel.h> |
| 30 | #include <profile.h> |
| 31 | #include <personmodel.h> |
| 32 | #include <globalinstances.h> |
| 33 | |
| 34 | // Ring Client |
| 35 | #include "avatarmanipulation.h" |
| 36 | #include "accountmigrationview.h" |
| 37 | #include "utils/models.h" |
| 38 | #include "native/pixbufmanipulator.h" |
| 39 | #include "video/video_widget.h" |
| 40 | |
| 41 | /* size of avatar */ |
| 42 | static constexpr int AVATAR_WIDTH = 100; /* px */ |
| 43 | static constexpr int AVATAR_HEIGHT = 100; /* px */ |
| 44 | |
| 45 | struct _AccountMigrationView |
| 46 | { |
| 47 | GtkBox parent; |
| 48 | }; |
| 49 | |
| 50 | struct _AccountMigrationViewClass |
| 51 | { |
| 52 | GtkBoxClass parent_class; |
| 53 | }; |
| 54 | |
| 55 | typedef struct _AccountMigrationViewPrivate AccountMigrationViewPrivate; |
| 56 | |
| 57 | struct _AccountMigrationViewPrivate |
| 58 | { |
| 59 | Account *account; |
| 60 | GtkWidget *stack_account_migration; |
| 61 | QMetaObject::Connection state_changed; |
| 62 | guint timeout_tag; |
| 63 | |
| 64 | /* generating_account_view */ |
| 65 | GtkWidget *migrating_account_view; |
| 66 | |
| 67 | /* error_view */ |
| 68 | GtkWidget *error_view; |
| 69 | GtkWidget *button_error_view_ok; |
| 70 | |
| 71 | /* main_view */ |
| 72 | GtkWidget *main_view; |
| 73 | GtkWidget *label_account_alias; |
| 74 | GtkWidget *label_account_id; |
| 75 | GtkWidget *image_avatar; |
| 76 | GtkWidget *label_password_error; |
| 77 | GtkWidget *entry_password; |
| 78 | GtkWidget *entry_password_confirm; |
| 79 | GtkWidget *button_migrate_account; |
| 80 | }; |
| 81 | |
| 82 | G_DEFINE_TYPE_WITH_PRIVATE(AccountMigrationView, account_migration_view, GTK_TYPE_BOX); |
| 83 | |
| 84 | #define ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_MIGRATION_VIEW_TYPE, AccountMigrationViewPrivate)) |
| 85 | |
| 86 | /* signals */ |
| 87 | enum { |
| 88 | ACCOUNT_MIGRATION_COMPLETED, |
| 89 | ACCOUNT_MIGRATION_FAILED, |
| 90 | LAST_SIGNAL |
| 91 | }; |
| 92 | |
| 93 | static guint account_migration_view_signals[LAST_SIGNAL] = { 0 }; |
| 94 | |
| 95 | static void |
| 96 | account_migration_view_dispose(GObject *object) |
| 97 | { |
| 98 | G_OBJECT_CLASS(account_migration_view_parent_class)->dispose(object); |
| 99 | } |
| 100 | |
| 101 | static void |
| 102 | account_migration_view_init(AccountMigrationView *view) |
| 103 | { |
| 104 | gtk_widget_init_template(GTK_WIDGET(view)); |
| 105 | } |
| 106 | |
| 107 | static void |
| 108 | account_migration_view_class_init(AccountMigrationViewClass *klass) |
| 109 | { |
| 110 | G_OBJECT_CLASS(klass)->dispose = account_migration_view_dispose; |
| 111 | |
| 112 | gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass), |
| 113 | "/cx/ring/RingGnome/accountmigrationview.ui"); |
| 114 | |
| 115 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, stack_account_migration); |
| 116 | |
| 117 | /* error_view */ |
| 118 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, error_view); |
| 119 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_error_view_ok); |
| 120 | |
| 121 | /* migrating_account_view */ |
| 122 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, migrating_account_view); |
| 123 | |
| 124 | /* main_view */ |
| 125 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, main_view); |
| 126 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_alias); |
| 127 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_account_id); |
| 128 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, image_avatar); |
| 129 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, label_password_error); |
| 130 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password); |
| 131 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, entry_password_confirm); |
| 132 | gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountMigrationView, button_migrate_account); |
| 133 | |
| 134 | /* add signals */ |
| 135 | account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED] = g_signal_new("account-migration-completed", |
| 136 | G_TYPE_FROM_CLASS(klass), |
| 137 | (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION), |
| 138 | 0, |
| 139 | nullptr, |
| 140 | nullptr, |
| 141 | g_cclosure_marshal_VOID__VOID, |
| 142 | G_TYPE_NONE, 0); |
| 143 | |
| 144 | account_migration_view_signals[ACCOUNT_MIGRATION_FAILED] = g_signal_new("account-migration-failed", |
| 145 | G_TYPE_FROM_CLASS(klass), |
| 146 | (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION), |
| 147 | 0, |
| 148 | nullptr, |
| 149 | nullptr, |
| 150 | g_cclosure_marshal_VOID__VOID, |
| 151 | G_TYPE_NONE, 0); |
| 152 | } |
| 153 | |
| 154 | static gboolean |
| 155 | migration_timeout(AccountMigrationView *view) |
| 156 | { |
| 157 | AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view); |
| 158 | QObject::disconnect(priv->state_changed); |
| 159 | gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->error_view); |
| 160 | return G_SOURCE_REMOVE; |
| 161 | } |
| 162 | |
| 163 | static void |
| 164 | button_error_view_ok_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view) |
| 165 | { |
| 166 | AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view); |
| 167 | g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_FAILED], 0); |
| 168 | } |
| 169 | |
| 170 | static void |
| 171 | migrate_account_clicked(G_GNUC_UNUSED GtkButton* button, AccountMigrationView *view) |
| 172 | { |
| 173 | AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view); |
| 174 | gtk_widget_hide(priv->label_password_error); |
| 175 | |
| 176 | /* Check for correct password */ |
| 177 | const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password)); |
| 178 | const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm)); |
| 179 | |
| 180 | if (g_strcmp0(password, password_confirm) != 0) |
| 181 | { |
| 182 | gtk_widget_show(priv->label_password_error); |
| 183 | return; |
| 184 | } |
| 185 | else |
| 186 | { |
| 187 | gtk_stack_set_visible_child(GTK_STACK(priv->stack_account_migration), priv->migrating_account_view); |
| 188 | |
| 189 | priv->account->setArchivePassword(password); |
| 190 | priv->account->performAction(Account::EditAction::SAVE); |
| 191 | |
| 192 | // Timeout in 30 seconds |
| 193 | priv->timeout_tag = g_timeout_add_full(G_PRIORITY_DEFAULT, 30000, (GSourceFunc)migration_timeout, view, NULL); |
| 194 | |
| 195 | priv->state_changed = QObject::connect( |
| 196 | priv->account, |
| 197 | &Account::stateChanged, |
| 198 | [=] (Account::RegistrationState state) { |
| 199 | switch(state) |
| 200 | { |
| 201 | case Account::RegistrationState::READY: |
| 202 | case Account::RegistrationState::TRYING: |
| 203 | case Account::RegistrationState::UNREGISTERED: |
| 204 | { |
| 205 | // Make sure that the account is ready to be displayed. |
| 206 | priv->account << Account::EditAction::RELOAD; |
| 207 | |
| 208 | AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view); |
| 209 | QObject::disconnect(priv->state_changed); // only want to emit once |
| 210 | g_source_remove(priv->timeout_tag); // We didn't timeout |
| 211 | g_signal_emit(G_OBJECT(view), account_migration_view_signals[ACCOUNT_MIGRATION_COMPLETED], 0); |
| 212 | break; |
| 213 | } |
| 214 | case Account::RegistrationState::ERROR: |
| 215 | case Account::RegistrationState::INITIALIZING: |
| 216 | case Account::RegistrationState::COUNT__: |
| 217 | { |
| 218 | // Keep waiting... |
| 219 | break; |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | ); |
| 224 | |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | static void |
| 229 | password_entry_changed(G_GNUC_UNUSED GtkEntry* entry, AccountMigrationView *view) |
| 230 | { |
| 231 | AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view); |
| 232 | |
| 233 | const gchar *password = gtk_entry_get_text(GTK_ENTRY(priv->entry_password)); |
| 234 | const gchar *password_confirm = gtk_entry_get_text(GTK_ENTRY(priv->entry_password_confirm)); |
| 235 | |
| 236 | if (strlen(password) > 0 && strlen(password_confirm) > 0) |
| 237 | { |
| 238 | gtk_widget_set_sensitive(priv->button_migrate_account, TRUE); |
| 239 | } |
| 240 | else |
| 241 | { |
| 242 | gtk_widget_set_sensitive(priv->button_migrate_account, FALSE); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | static void |
| 247 | build_migration_view(AccountMigrationView *view) |
| 248 | { |
| 249 | g_return_if_fail(IS_ACCOUNT_MIGRATION_VIEW(view)); |
| 250 | AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view); |
| 251 | |
| 252 | g_signal_connect(priv->button_migrate_account, "clicked", G_CALLBACK(migrate_account_clicked), view); |
| 253 | g_signal_connect(priv->button_error_view_ok, "clicked", G_CALLBACK(button_error_view_ok_clicked), view); |
| 254 | g_signal_connect(priv->entry_password, "changed", G_CALLBACK(password_entry_changed), view); |
| 255 | g_signal_connect(priv->entry_password_confirm, "changed", G_CALLBACK(password_entry_changed), view); |
| 256 | |
| 257 | gtk_label_set_text(GTK_LABEL(priv->label_account_alias), priv->account->alias().toUtf8().constData()); |
| 258 | gtk_label_set_text(GTK_LABEL(priv->label_account_id), priv->account->id().constData()); |
| 259 | |
| 260 | /* set the avatar picture */ |
| 261 | auto photo = GlobalInstances::pixmapManipulator().contactPhoto( |
| 262 | ProfileModel::instance().selectedProfile()->person(), |
| 263 | QSize(AVATAR_WIDTH, AVATAR_HEIGHT), |
| 264 | false); |
| 265 | std::shared_ptr<GdkPixbuf> pixbuf_photo = photo.value<std::shared_ptr<GdkPixbuf>>(); |
| 266 | |
| 267 | if (photo.isValid()) { |
| 268 | gtk_image_set_from_pixbuf (GTK_IMAGE(priv->image_avatar), pixbuf_photo.get()); |
| 269 | } else { |
| 270 | g_warning("invalid pixbuf"); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | GtkWidget * |
| 275 | account_migration_view_new(Account* account) |
| 276 | { |
| 277 | gpointer view = g_object_new(ACCOUNT_MIGRATION_VIEW_TYPE, NULL); |
| 278 | AccountMigrationViewPrivate *priv = ACCOUNT_MIGRATION_VIEW_GET_PRIVATE(view); |
| 279 | priv->account = account; |
| 280 | build_migration_view(ACCOUNT_MIGRATION_VIEW(view)); |
| 281 | return (GtkWidget *)view; |
| 282 | } |