blob: dd57868b507501ae5c0cbd9a5ac7b4f576a3f20f [file] [log] [blame]
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04001/*
Stepan Salenikovichbe87d2c2016-01-25 14:14:34 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Stepan Salenikovich61cbab02015-03-16 18:35:10 -04003 * Author: Stepan Salenikovich <stepan.salenikovich@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.
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040018 */
19
20#include "accountview.h"
21
22#include <gtk/gtk.h>
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -040023#include <glib/gi18n.h>
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040024#include <accountmodel.h>
Edric Milaret58e71072016-03-21 12:16:37 -040025#include <codecmodel.h>
Stepan Salenikovichc3fede22015-03-20 17:01:47 -040026#include <protocolmodel.h>
27#include <QtCore/QItemSelectionModel>
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040028#include "models/gtkqtreemodel.h"
Stepan Salenikovichc3fede22015-03-20 17:01:47 -040029#include "models/gtkqsortfiltertreemodel.h"
30#include "models/activeitemproxymodel.h"
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040031#include "accountgeneraltab.h"
32#include "accountaudiotab.h"
33#include "accountvideotab.h"
Stepan Salenikovichb7e6a732015-05-04 17:13:22 -040034#include "accountadvancedtab.h"
Stepan Salenikovich7a1e71c2015-05-07 11:14:48 -040035#include "accountsecuritytab.h"
Stepan Salenikovichbd029582015-03-24 11:00:56 -040036#include "dialogs.h"
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -040037#include <glib/gprintf.h>
Stepan Salenikovich9816a942015-04-22 17:49:16 -040038#include "utils/models.h"
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040039
40struct _AccountView
41{
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050042 GtkPaned parent;
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040043};
44
45struct _AccountViewClass
46{
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050047 GtkPanedClass parent_class;
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040048};
49
50typedef struct _AccountViewPrivate AccountViewPrivate;
51
52struct _AccountViewPrivate
53{
54 GtkWidget *treeview_account_list;
55 GtkWidget *stack_account;
56 GtkWidget *current_account_notebook;
Stepan Salenikovichc3fede22015-03-20 17:01:47 -040057 GtkWidget *button_remove_account;
58 GtkWidget *button_add_account;
59 GtkWidget *combobox_account_type;
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040060
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040061 gint current_page; /* keeps track of current notebook page displayed */
Stepan Salenikovichc3fede22015-03-20 17:01:47 -040062
63 ActiveItemProxyModel *active_protocols;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040064 QMetaObject::Connection protocol_selection_changed;
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040065};
66
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -050067G_DEFINE_TYPE_WITH_PRIVATE(AccountView, account_view, GTK_TYPE_PANED);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040068
69#define ACCOUNT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_VIEW_TYPE, AccountViewPrivate))
70
71static void
72account_view_dispose(GObject *object)
73{
74 AccountView *view = ACCOUNT_VIEW(object);
75 AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
76
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040077 QObject::disconnect(priv->protocol_selection_changed);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040078
79 G_OBJECT_CLASS(account_view_parent_class)->dispose(object);
80}
81
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -040082static void
83account_view_finalize(GObject *object)
84{
85 AccountView *view = ACCOUNT_VIEW(object);
86 AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
87
88 delete priv->active_protocols;
89
90 G_OBJECT_CLASS(account_view_parent_class)->finalize(object);
91}
92
Stepan Salenikovich61cbab02015-03-16 18:35:10 -040093static void
Stepan Salenikovichc3fede22015-03-20 17:01:47 -040094update_account_model_selection(GtkTreeSelection *selection, G_GNUC_UNUSED gpointer user_data)
95{
96 QModelIndex current = get_index_from_selection(selection);
97 if (current.isValid())
Guillaume Roguez5d1514b2015-10-22 15:55:31 -040098 AccountModel::instance().selectionModel()->setCurrentIndex(current, QItemSelectionModel::ClearAndSelect);
Stepan Salenikovichc3fede22015-03-20 17:01:47 -040099 else
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400100 AccountModel::instance().selectionModel()->clearCurrentIndex();
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400101}
102
Stepan Salenikovichd45e6392015-08-11 10:16:35 -0400103static GtkWidget *
104create_scrolled_account_view(GtkWidget *account_view)
105{
106 auto scrolled = gtk_scrolled_window_new(NULL, NULL);
107 gtk_container_add(GTK_CONTAINER(scrolled), account_view);
108 return scrolled;
109}
110
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400111static void
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400112account_selection_changed(GtkTreeSelection *selection, AccountView *view)
113{
114 g_return_if_fail(IS_ACCOUNT_VIEW(view));
115 AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
116
117 GtkWidget *old_account_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
118
119 /* keep track of the last tab displayed */
120 if (priv->current_account_notebook)
121 priv->current_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->current_account_notebook));
122
123 if (priv->current_page < 0)
124 priv->current_page = 0;
125
126 QModelIndex account_idx = get_index_from_selection(selection);
127 if (!account_idx.isValid()) {
128 /* it nothing is slected, simply display something empty */
129 GtkWidget *empty_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
130 gtk_widget_show(empty_box);
131 gtk_stack_add_named(GTK_STACK(priv->stack_account), empty_box, "placeholder");
132 gtk_stack_set_visible_child(GTK_STACK(priv->stack_account), empty_box);
133 priv->current_account_notebook = NULL;
134 } else {
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400135 Account *account = AccountModel::instance().getAccountByModelIndex(account_idx);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400136
137 /* build new account view */
138 GtkWidget *hbox_account = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
139
140 /* create account notebook */
141 priv->current_account_notebook = gtk_notebook_new();
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -0500142 gtk_notebook_set_scrollable(GTK_NOTEBOOK(priv->current_account_notebook), TRUE);
143 gtk_notebook_set_show_border(GTK_NOTEBOOK(priv->current_account_notebook), FALSE);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400144 gtk_box_pack_start(GTK_BOX(hbox_account), priv->current_account_notebook, TRUE, TRUE, 0);
145
146 /* customize account view based on account */
Stepan Salenikovichd45e6392015-08-11 10:16:35 -0400147 auto general_tab = create_scrolled_account_view(account_general_tab_new(account));
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400148 gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
149 general_tab,
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400150 gtk_label_new(C_("Account settings", "General")));
Stepan Salenikovichd45e6392015-08-11 10:16:35 -0400151 auto audio_tab = create_scrolled_account_view(account_audio_tab_new(account));
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400152 gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
153 audio_tab,
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400154 gtk_label_new(C_("Account settings", "Audio")));
Stepan Salenikovichd45e6392015-08-11 10:16:35 -0400155 auto video_tab = create_scrolled_account_view(account_video_tab_new(account));
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400156 gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
157 video_tab,
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400158 gtk_label_new(C_("Account settings", "Video")));
Stepan Salenikovichd45e6392015-08-11 10:16:35 -0400159 auto advanced_tab = create_scrolled_account_view(account_advanced_tab_new(account));
Stepan Salenikovichb7e6a732015-05-04 17:13:22 -0400160 gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
161 advanced_tab,
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400162 gtk_label_new(C_("Account settings", "Advanced")));
Stepan Salenikovichd45e6392015-08-11 10:16:35 -0400163 auto security_tab = create_scrolled_account_view(account_security_tab_new(account));
Stepan Salenikovich7a1e71c2015-05-07 11:14:48 -0400164 gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
165 security_tab,
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400166 gtk_label_new(C_("Account settings", "Security")));
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400167
Stepan Salenikovichad589082016-05-18 13:22:52 -0400168 gtk_widget_show_all(hbox_account);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400169 /* set the tab displayed to the same as the prev account selected */
170 gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->current_account_notebook), priv->current_page);
171
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400172 /* set the new account view as visible */
173 char *account_view_name = g_strdup_printf("%p_account", account);
174 gtk_stack_add_named(GTK_STACK(priv->stack_account), hbox_account, account_view_name);
175 gtk_stack_set_visible_child(GTK_STACK(priv->stack_account), hbox_account);
176 g_free(account_view_name);
177 }
178
179 /* remove the old account view */
180 if (old_account_view)
181 gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_account_view);
182}
183
184static void
185account_active_toggled(GtkCellRendererToggle *renderer, gchar *path, AccountView *view)
186{
187 g_return_if_fail(IS_ACCOUNT_VIEW(view));
188 AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
189
190 /* we want to set it to the opposite of the current value */
191 gboolean toggle = !gtk_cell_renderer_toggle_get_active(renderer);
192
193 /* get iter which was clicked */
194 GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
195 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_account_list));
196 GtkTreeIter iter;
197 gtk_tree_model_get_iter(model, &iter, tree_path);
198
199 /* get qmodelindex from iter and set the model data */
200 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
201 if (idx.isValid()) {
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400202 QVariant alias = idx.data(static_cast<int>(Account::Role::Alias));
Stepan Salenikovichfea84052016-05-13 16:32:51 -0400203 AccountModel::instance().setData(idx, QVariant(toggle), Qt::CheckStateRole);
204 /* save the account to apply the changed state right away */
205 AccountModel::instance().getAccountByModelIndex(idx)->performAction(Account::EditAction::SAVE);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400206 }
207}
208
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400209static gboolean
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400210remove_account_dialog(AccountView *view, Account *account)
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400211{
212 gboolean response = FALSE;
213 GtkWidget *dialog = gtk_message_dialog_new(NULL,
214 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
215 GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400216 _("Are you sure you want to delete account \"%s\"?"),
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400217 account->alias().toLocal8Bit().constData());
218
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400219 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
220
221 /* get parent window so we can center on it */
222 GtkWidget *parent = gtk_widget_get_toplevel(GTK_WIDGET(view));
223 if (gtk_widget_is_toplevel(parent)) {
224 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent));
225 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
226 }
227
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400228 switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
229 case GTK_RESPONSE_OK:
230 response = TRUE;
231 break;
232 default:
233 response = FALSE;
234 break;
235 }
236
237 gtk_widget_destroy(dialog);
238
239 return response;
240}
241
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400242static gboolean
243save_account(GtkWidget *working_dialog)
244{
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400245 AccountModel::instance().save();
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400246 if (working_dialog)
247 gtk_widget_destroy(working_dialog);
248
249 return G_SOURCE_REMOVE;
250}
251
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400252static void
253remove_account(G_GNUC_UNUSED GtkWidget *entry, AccountView *view)
254{
255 g_return_if_fail(IS_ACCOUNT_VIEW(view));
256 AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
257
258 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
259 QModelIndex idx = get_index_from_selection(selection);
260
261 if (idx.isValid()) {
262 /* this is a destructive operation, ask the user if they are sure */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400263 Account *account = AccountModel::instance().getAccountByModelIndex(idx);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400264 if (remove_account_dialog(view, account)) {
265 /* show working dialog in case save operation takes time */
266 GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
267 gtk_window_present(GTK_WINDOW(working));
268
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400269 AccountModel::instance().remove(idx);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400270
271 /* now save the time it takes to transition the account view to the new account (300ms)
272 * the save doesn't happen before the "working" dialog is presented
273 * the timeout function should destroy the "working" dialog when done saving
274 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400275 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400276 }
277 }
278}
279
280static void
281add_account(G_GNUC_UNUSED GtkWidget *entry, AccountView *view)
282{
283 g_return_if_fail(IS_ACCOUNT_VIEW(view));
284 AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
285
286 GtkTreeIter protocol_iter;
287 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->combobox_account_type), &protocol_iter)) {
288 /* get the qmodelindex of the protocol */
289 GtkTreeModel *protocol_model = gtk_combo_box_get_model(GTK_COMBO_BOX(priv->combobox_account_type));
290 QModelIndex protocol_idx = gtk_q_sort_filter_tree_model_get_source_idx(
291 GTK_Q_SORT_FILTER_TREE_MODEL(protocol_model),
292 &protocol_iter);
293 if (protocol_idx.isValid()) {
294 protocol_idx = priv->active_protocols->mapToSource(protocol_idx);
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400295
296 /* show working dialog in case save operation takes time */
297 GtkWidget *working = ring_dialog_working(GTK_WIDGET(view), NULL);
298 gtk_window_present(GTK_WINDOW(working));
299
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400300 auto account = AccountModel::instance().add(QString(_("New Account")), protocol_idx);
Stepan Salenikovich75a39172015-07-10 13:21:08 -0400301 if (account->protocol() == Account::Protocol::RING)
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400302 account->setDisplayName(_("New Account"));
Stepan Salenikovichbd029582015-03-24 11:00:56 -0400303
304 /* now save after a short timeout to make sure that
305 * the save doesn't happen before the "working" dialog is presented
306 * the timeout function should destroy the "working" dialog when done saving
307 */
Stepan Salenikovich12fee942015-03-25 18:38:47 -0400308 g_timeout_add_full(G_PRIORITY_DEFAULT, 300, (GSourceFunc)save_account, working, NULL);
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400309 }
310 }
311}
312
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400313static void
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -0400314state_to_string(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
315 GtkCellRenderer *cell,
316 GtkTreeModel *tree_model,
317 GtkTreeIter *iter,
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400318 GtkTreeView *treeview)
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -0400319{
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400320 // check if this iter is selected
321 gboolean is_selected = FALSE;
322 if (GTK_IS_TREE_VIEW(treeview)) {
323 auto selection = gtk_tree_view_get_selection(treeview);
324 is_selected = gtk_tree_selection_iter_is_selected(selection, iter);
325 }
326
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -0400327 gchar *display_state = NULL;
Stepan Salenikoviche7a73142015-08-11 11:55:59 -0400328
329 /* get account */
330 QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(tree_model), iter);
331 if (idx.isValid()) {
332
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400333 auto account = AccountModel::instance().getAccountByModelIndex(idx);
Stepan Salenikoviche7a73142015-08-11 11:55:59 -0400334 auto humanState = account->toHumanStateName();
335
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400336 auto escaped_state = g_markup_escape_text(humanState.toUtf8().constData(), -1);
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400337 /* we want the color of the status text to be the default color if this iter is
338 * selected so that the treeview is able to invert it against the selection color */
339 if (is_selected) {
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400340 display_state = escaped_state;
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400341 } else {
342 switch (account->registrationState()) {
343 case Account::RegistrationState::READY:
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400344 display_state = g_strdup_printf("<span fgcolor=\"green\">%s</span>", escaped_state);
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400345 break;
346 case Account::RegistrationState::UNREGISTERED:
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400347 display_state = g_strdup_printf("<span fgcolor=\"gray\">%s</span>", escaped_state);
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400348 break;
349 case Account::RegistrationState::TRYING:
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400350 display_state = g_strdup_printf("<span fgcolor=\"orange\">%s</span>", escaped_state);
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400351 break;
352 case Account::RegistrationState::ERROR:
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400353 display_state = g_strdup_printf("<span fgcolor=\"red\">%s</span>", escaped_state);
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400354 break;
355 case Account::RegistrationState::COUNT__:
356 g_warning("registration state should never be \"count\"");
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400357 display_state = g_strdup_printf("<span fgcolor=\"red\">%s</span>", escaped_state);
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400358 break;
359 }
Stepan Salenikovich0731de02016-05-17 17:47:16 -0400360 g_free(escaped_state);
Stepan Salenikoviche7a73142015-08-11 11:55:59 -0400361 }
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -0400362 }
Stepan Salenikoviche7a73142015-08-11 11:55:59 -0400363
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -0400364 g_object_set(G_OBJECT(cell), "markup", display_state, NULL);
365 g_free(display_state);
366}
367
368static void
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400369account_view_init(AccountView *view)
370{
371 gtk_widget_init_template(GTK_WIDGET(view));
372
373 AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
374
375 /* account model */
376 GtkQTreeModel *account_model;
377 GtkCellRenderer *renderer;
378 GtkTreeViewColumn *column;
379
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400380 account_model = gtk_q_tree_model_new(&AccountModel::instance(), 4,
aviau9bbb19b2016-05-16 15:53:44 -0400381 0, Account::Role::Enabled, G_TYPE_BOOLEAN,
382 0, Account::Role::Alias, G_TYPE_STRING,
383 0, Account::Role::Proto, G_TYPE_STRING,
384 0, Account::Role::RegistrationState, G_TYPE_UINT);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400385 gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_account_list), GTK_TREE_MODEL(account_model));
386
387 renderer = gtk_cell_renderer_toggle_new();
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400388 column = gtk_tree_view_column_new_with_attributes(C_("Account state column", "Enabled"), renderer, "active", 0, NULL);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400389 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_account_list), column);
390
391 g_signal_connect(renderer, "toggled", G_CALLBACK(account_active_toggled), view);
392
393 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400394 column = gtk_tree_view_column_new_with_attributes(C_("Account alias (name) column", "Alias"), renderer, "text", 1, NULL);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400395 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_account_list), column);
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -0500396 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
397 gtk_tree_view_column_set_expand(column, TRUE);
398 // set a min width so most of the account name is visible
399 g_object_set(G_OBJECT(renderer), "width", 75, NULL);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400400
401 renderer = gtk_cell_renderer_text_new();
Stepan Salenikovicha1b8cb32015-09-11 14:58:35 -0400402 column = gtk_tree_view_column_new_with_attributes(C_("Account status column", "Status"), renderer, "text", 3, NULL);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400403 gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_account_list), column);
Stepan Salenikovichab0f5be2016-02-05 15:16:35 -0500404 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
405 gtk_tree_view_column_set_expand(column, TRUE);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400406
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -0400407 /* the registration state is an enum, we want to display it as a string */
408 gtk_tree_view_column_set_cell_data_func(
409 column,
410 renderer,
411 (GtkTreeCellDataFunc)state_to_string,
Stepan Salenikoviche4981b22015-10-22 15:22:59 -0400412 priv->treeview_account_list,
Stepan Salenikovichd6a9ba92015-03-29 11:14:36 -0400413 NULL);
414
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400415 /* add an empty box to the account stack initially, otherwise there will
416 * be no cool animation when the first account is selected */
417 GtkWidget *empty_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
418 gtk_widget_show(empty_box);
419 gtk_stack_add_named(GTK_STACK(priv->stack_account), empty_box, "placeholder");
420 gtk_stack_set_visible_child(GTK_STACK(priv->stack_account), empty_box);
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400421
422 /* populate account type combo box */
423 /* TODO: when to delete this model? */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400424 priv->active_protocols = new ActiveItemProxyModel((QAbstractItemModel *)AccountModel::instance().protocolModel());
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400425
426 GtkQSortFilterTreeModel *protocol_model = gtk_q_sort_filter_tree_model_new(
427 (QSortFilterProxyModel *)priv->active_protocols,
428 1,
aviau271bcc22016-05-27 17:25:19 -0400429 0, Qt::DisplayRole, G_TYPE_STRING);
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400430
431 gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_account_type), GTK_TREE_MODEL(protocol_model));
432
433 renderer = gtk_cell_renderer_text_new();
434 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_account_type), renderer, FALSE);
435 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(priv->combobox_account_type), renderer,
436 "text", 0, NULL);
437
438 /* connect signals to and from the selection model of the account model */
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400439 priv->protocol_selection_changed = QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400440 AccountModel::instance().selectionModel(),
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400441 &QItemSelectionModel::currentChanged,
Stepan Salenikovich825bb0a2016-03-11 15:33:52 -0500442 [=](const QModelIndex & current, G_GNUC_UNUSED const QModelIndex & previous) {
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400443 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
444
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400445 /* select the current */
446 if (current.isValid()) {
447 GtkTreeIter new_iter;
448 if (gtk_q_tree_model_source_index_to_iter(account_model, current, &new_iter)) {
449 gtk_tree_selection_select_iter(selection, &new_iter);
450 } else {
Stepan Salenikovichf903d1b2015-03-25 14:51:45 -0400451 g_warning("SelectionModel of AccountModel changed to invalid QModelIndex?");
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400452 }
453 }
454 }
455 );
456
457 GtkTreeSelection *account_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
458 g_signal_connect(account_selection, "changed", G_CALLBACK(update_account_model_selection), NULL);
459 g_signal_connect(account_selection, "changed", G_CALLBACK(account_selection_changed), view);
460
461 /* select the default protocol */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400462 QModelIndex protocol_idx = AccountModel::instance().protocolModel()->selectionModel()->currentIndex();
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400463 if (protocol_idx.isValid()) {
464 protocol_idx = priv->active_protocols->mapFromSource(protocol_idx);
465 GtkTreeIter protocol_iter;
466 if (gtk_q_sort_filter_tree_model_source_index_to_iter(
467 (GtkQSortFilterTreeModel *)protocol_model,
468 protocol_idx,
469 &protocol_iter)) {
470 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(priv->combobox_account_type), &protocol_iter);
471 }
472 }
473
474 /* connect signals to add/remove accounts */
475 g_signal_connect(priv->button_remove_account, "clicked", G_CALLBACK(remove_account), view);
476 g_signal_connect(priv->button_add_account, "clicked", G_CALLBACK(add_account), view);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400477}
478
479static void
480account_view_class_init(AccountViewClass *klass)
481{
482 G_OBJECT_CLASS(klass)->dispose = account_view_dispose;
Stepan Salenikovich2d63d5e2015-03-22 23:23:54 -0400483 G_OBJECT_CLASS(klass)->finalize = account_view_finalize;
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400484
485 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
486 "/cx/ring/RingGnome/accountview.ui");
487
488 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, treeview_account_list);
489 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, stack_account);
Stepan Salenikovichc3fede22015-03-20 17:01:47 -0400490 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, button_remove_account);
491 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, button_add_account);
492 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, combobox_account_type);
Stepan Salenikovich61cbab02015-03-16 18:35:10 -0400493}
494
495GtkWidget *
496account_view_new(void)
497{
498 return (GtkWidget *)g_object_new(ACCOUNT_VIEW_TYPE, NULL);
499}