gnome: add account settings
Refs #68670
Change-Id: I24684d1562a27180dcf684747e90d8c92cc07250
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 39cb6ba..8a18197 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -140,6 +140,14 @@
src/backends/minimalhistorybackend.cpp
src/models/gtkqsortfiltertreemodel.h
src/models/gtkqsortfiltertreemodel.cpp
+ src/accountview.h
+ src/accountview.cpp
+ src/accountgeneraltab.h
+ src/accountgeneraltab.cpp
+ src/accountaudiotab.h
+ src/accountaudiotab.cpp
+ src/accountvideotab.h
+ src/accountvideotab.cpp
)
# compile glib resource files to c code
diff --git a/src/accountaudiotab.cpp b/src/accountaudiotab.cpp
new file mode 100644
index 0000000..cb3388e
--- /dev/null
+++ b/src/accountaudiotab.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#include "accountaudiotab.h"
+
+#include <gtk/gtk.h>
+#include <account.h>
+#include <audio/codecmodel.h>
+#include "models/gtkqsortfiltertreemodel.h"
+
+struct _AccountAudioTab
+{
+ GtkBox parent;
+};
+
+struct _AccountAudioTabClass
+{
+ GtkBoxClass parent_class;
+};
+
+typedef struct _AccountAudioTabPrivate AccountAudioTabPrivate;
+
+struct _AccountAudioTabPrivate
+{
+ Account *account;
+ GtkWidget *treeview_codecs;
+ GtkWidget *button_moveup;
+ GtkWidget *button_movedown;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountAudioTab, account_audio_tab, GTK_TYPE_BOX);
+
+#define ACCOUNT_AUDIO_TAB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_AUDIO_TAB_TYPE, AccountAudioTabPrivate))
+
+static void
+account_audio_tab_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(account_audio_tab_parent_class)->dispose(object);
+}
+
+static void
+account_audio_tab_init(AccountAudioTab *view)
+{
+ gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_audio_tab_class_init(AccountAudioTabClass *klass)
+{
+ G_OBJECT_CLASS(klass)->dispose = account_audio_tab_dispose;
+
+ gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+ "/cx/ring/RingGnome/accountaudiotab.ui");
+
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountAudioTab, treeview_codecs);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountAudioTab, button_moveup);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountAudioTab, button_movedown);
+}
+
+static void
+codec_active_toggled(GtkCellRendererToggle *renderer, gchar *path, AccountAudioTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_AUDIO_TAB(view));
+ AccountAudioTabPrivate *priv = ACCOUNT_AUDIO_TAB_GET_PRIVATE(view);
+
+ /* we want to set it to the opposite of the current value */
+ gboolean toggle = !gtk_cell_renderer_toggle_get_active(renderer);
+
+ /* get iter which was clicked */
+ GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_codecs));
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter(model, &iter, tree_path);
+
+ /* get qmodelindex from iter and set the model data */
+ QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(model), &iter);
+ if (idx.isValid()) {
+ priv->account->codecModel()->audioCodecs()->setData(idx, QVariant(toggle), Qt::CheckStateRole);
+ priv->account->codecModel()->save();
+ }
+}
+
+static QModelIndex
+get_index_from_selection(GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ return gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(model), &iter);
+ } else {
+ return QModelIndex();
+ }
+}
+
+static void
+move_selected_codec(AccountAudioTab *view, int position_diff)
+{
+ g_return_if_fail(IS_ACCOUNT_AUDIO_TAB(view));
+ AccountAudioTabPrivate *priv = ACCOUNT_AUDIO_TAB_GET_PRIVATE(view);
+
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_codecs));
+ QModelIndex idx = get_index_from_selection(selection);
+
+ if(!idx.isValid())
+ return;
+
+ QMimeData* mime = priv->account->codecModel()->audioCodecs()->mimeData(QModelIndexList() << idx);
+ priv->account->codecModel()->audioCodecs()->dropMimeData(
+ mime,
+ Qt::MoveAction,
+ idx.row() + position_diff,
+ 0,
+ QModelIndex());
+ priv->account->saveCodecs();
+
+ /* now make sure to select the same codec which was moved
+ * TODO: UGLY! this should be somehow done in the qt modle bindings,
+ * or via a selection model, not here
+ */
+ int new_row = idx.row() + position_diff;
+ int row_count = priv->account->codecModel()->audioCodecs()->rowCount(idx.parent());
+ if (new_row < 0)
+ new_row = row_count - 1;
+ else if (new_row >= row_count)
+ new_row = 0;
+
+ idx = idx.sibling(new_row, idx.column());
+ GtkTreeIter iter;
+ if (gtk_q_sort_filter_tree_model_source_index_to_iter(
+ GTK_Q_SORT_FILTER_TREE_MODEL(gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_codecs))),
+ idx,
+ &iter)) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+}
+
+static void
+move_codec_up(G_GNUC_UNUSED GtkButton *button, AccountAudioTab *view)
+{
+ move_selected_codec(view, -1);
+}
+
+static void
+move_codec_down(G_GNUC_UNUSED GtkButton *button, AccountAudioTab *view)
+{
+ move_selected_codec(view, +1);
+}
+
+static void
+build_tab_view(AccountAudioTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_AUDIO_TAB(view));
+ AccountAudioTabPrivate *priv = ACCOUNT_AUDIO_TAB_GET_PRIVATE(view);
+
+ /* codec model */
+ GtkQSortFilterTreeModel *codec_model;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ codec_model = gtk_q_sort_filter_tree_model_new(
+ priv->account->codecModel()->audioCodecs(),
+ 4,
+ Qt::CheckStateRole, G_TYPE_BOOLEAN,
+ CodecModel::Role::NAME, G_TYPE_STRING,
+ CodecModel::Role::BITRATE, G_TYPE_STRING,
+ CodecModel::Role::SAMPLERATE, G_TYPE_STRING);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_codecs), GTK_TREE_MODEL(codec_model));
+
+ renderer = gtk_cell_renderer_toggle_new();
+ column = gtk_tree_view_column_new_with_attributes("Enabled", renderer, "active", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_codecs), column);
+
+ g_signal_connect(renderer, "toggled", G_CALLBACK(codec_active_toggled), view);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 1, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_codecs), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Bitrate", renderer, "text", 2, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_codecs), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Samplerate", renderer, "text", 3, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_codecs), column);
+
+ /* connect move codecs up/down signals */
+ g_signal_connect(priv->button_moveup, "clicked", G_CALLBACK(move_codec_up), view);
+ g_signal_connect(priv->button_movedown, "clicked", G_CALLBACK(move_codec_down), view);
+}
+
+GtkWidget *
+account_audio_tab_new(Account *account)
+{
+ g_return_val_if_fail(account != NULL, NULL);
+
+ gpointer view = g_object_new(ACCOUNT_AUDIO_TAB_TYPE, NULL);
+
+ AccountAudioTabPrivate *priv = ACCOUNT_AUDIO_TAB_GET_PRIVATE(view);
+ priv->account = account;
+
+ build_tab_view(ACCOUNT_AUDIO_TAB(view));
+
+ return (GtkWidget *)view;
+}
diff --git a/src/accountaudiotab.h b/src/accountaudiotab.h
new file mode 100644
index 0000000..6040137
--- /dev/null
+++ b/src/accountaudiotab.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#ifndef _ACCOUNTAUDIOTAB_H
+#define _ACCOUNTAUDIOTAB_H
+
+#include <gtk/gtk.h>
+#include <account.h>
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_AUDIO_TAB_TYPE (account_audio_tab_get_type ())
+#define ACCOUNT_AUDIO_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_AUDIO_TAB_TYPE, AccountAudioTab))
+#define ACCOUNT_AUDIO_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_AUDIO_TAB_TYPE, AccountAudioTabClass))
+#define IS_ACCOUNT_AUDIO_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_AUDIO_TAB_TYPE))
+#define IS_ACCOUNT_AUDIO_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_AUDIO_TAB_TYPE))
+
+typedef struct _AccountAudioTab AccountAudioTab;
+typedef struct _AccountAudioTabClass AccountAudioTabClass;
+
+GType account_audio_tab_get_type (void) G_GNUC_CONST;
+GtkWidget *account_audio_tab_new (Account *account);
+
+G_END_DECLS
+
+#endif /* _ACCOUNTAUDIOTAB_H */
\ No newline at end of file
diff --git a/src/accountgeneraltab.cpp b/src/accountgeneraltab.cpp
new file mode 100644
index 0000000..f24c233
--- /dev/null
+++ b/src/accountgeneraltab.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#include "accountgeneraltab.h"
+
+#include <gtk/gtk.h>
+#include <account.h>
+
+struct _AccountGeneralTab
+{
+ GtkBox parent;
+};
+
+struct _AccountGeneralTabClass
+{
+ GtkBoxClass parent_class;
+};
+
+typedef struct _AccountGeneralTabPrivate AccountGeneralTabPrivate;
+
+struct _AccountGeneralTabPrivate
+{
+ Account *account;
+ GtkWidget *grid_account;
+ GtkWidget *grid_parameters;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountGeneralTab, account_general_tab, GTK_TYPE_BOX);
+
+#define ACCOUNT_GENERAL_TAB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_GENERAL_TAB_TYPE, AccountGeneralTabPrivate))
+
+static void
+account_general_tab_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(account_general_tab_parent_class)->dispose(object);
+}
+
+static void
+account_general_tab_init(AccountGeneralTab *view)
+{
+ gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_general_tab_class_init(AccountGeneralTabClass *klass)
+{
+ G_OBJECT_CLASS(klass)->dispose = account_general_tab_dispose;
+
+ gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+ "/cx/ring/RingGnome/accountgeneraltab.ui");
+
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, grid_account);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountGeneralTab, grid_parameters);
+}
+
+static void
+account_alias_changed(GtkEditable *entry, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setAlias(QString(gtk_editable_get_chars(entry, 0, -1)));
+}
+
+static void
+account_hostname_changed(GtkEditable *entry, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setHostname(QString(gtk_editable_get_chars(entry, 0, -1)));
+}
+
+static void
+account_username_changed(GtkEditable *entry, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setUsername(QString(gtk_editable_get_chars(entry, 0, -1)));
+}
+
+static void
+account_password_changed(GtkEditable *entry, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setPassword(QString(gtk_editable_get_chars(entry, 0, -1)));
+}
+
+static void
+show_password(GtkToggleButton *checkbutton, GtkEntry *entry)
+{
+ gtk_entry_set_visibility(GTK_ENTRY(entry), gtk_toggle_button_get_active(checkbutton));
+}
+
+static void
+account_proxy_changed(GtkEditable *entry, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setProxy(QString(gtk_editable_get_chars(entry, 0, -1)));
+}
+
+static void
+account_mailbox_changed(GtkEditable *entry, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setMailbox(QString(gtk_editable_get_chars(entry, 0, -1)));
+}
+
+static void
+auto_answer(GtkToggleButton *checkbutton, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setAutoAnswer(gtk_toggle_button_get_active(checkbutton));
+}
+
+static void
+upnp_enabled(GtkToggleButton *checkbutton, AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account->setUpnpEnabled(gtk_toggle_button_get_active(checkbutton));
+}
+
+
+static void
+build_tab_view(AccountGeneralTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_GENERAL_TAB(view));
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+
+ int grid_row = 0;
+ GtkWidget *label = NULL;
+ GtkWidget *entry = NULL;
+ GtkWidget *checkbutton = NULL;
+
+ /* build account grid */
+
+ /* check if its ip2ip account */
+ const QByteArray& alias = priv->account->alias().toLocal8Bit();
+
+ if (strcmp(alias.constData(), "IP2IP") == 0) {
+ label = gtk_label_new("IP2IP");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_account), label, 0, grid_row, 1, 1);
+ ++grid_row;
+ } else {
+ /* account alias */
+ label = gtk_label_new("Alias");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_account), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), alias.constData());
+ gtk_widget_set_halign(entry, GTK_ALIGN_START);
+ g_signal_connect(entry, "changed", G_CALLBACK(account_alias_changed), view);
+ gtk_grid_attach(GTK_GRID(priv->grid_account), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+
+ /* account type */
+ label = gtk_label_new("Type");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_account), label, 0, grid_row, 1, 1);
+
+ label = gtk_label_new("");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ switch (priv->account->protocol()) {
+ case Account::Protocol::SIP:
+ gtk_label_set_text(GTK_LABEL(label), "SIP");
+ break;
+ case Account::Protocol::IAX:
+ gtk_label_set_text(GTK_LABEL(label), "IAX");
+ break;
+ case Account::Protocol::RING:
+ gtk_label_set_text(GTK_LABEL(label), "RING");
+ break;
+ case Account::Protocol::COUNT__:
+ break;
+ }
+
+ gtk_grid_attach(GTK_GRID(priv->grid_account), label, 1, grid_row, 1, 1);
+ ++grid_row;
+ }
+
+ if (priv->account->protocol() == Account::Protocol::RING) {
+ label = gtk_label_new("Hash");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_account), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), priv->account->username().toLocal8Bit().constData());
+ g_object_set(G_OBJECT(entry), "editable", FALSE, NULL);
+ g_object_set(G_OBJECT(entry), "max-width-chars", 50, NULL);
+ gtk_widget_override_font(entry, pango_font_description_from_string("monospace"));
+ gtk_entry_set_alignment(GTK_ENTRY(entry), 0.5);
+ gtk_grid_attach(GTK_GRID(priv->grid_account), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+ }
+
+ gtk_widget_show_all(priv->grid_account);
+
+ /* build parameters grid */
+ grid_row = 0;
+ if (strcmp(alias.constData(), "IP2IP") != 0) {
+ if (priv->account->protocol() != Account::Protocol::RING) {
+ /* SIP and IAX have the same params */
+
+ /* host name */
+ label = gtk_label_new("Host name");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), priv->account->hostname().toLocal8Bit().constData());
+ g_signal_connect(entry, "changed", G_CALLBACK(account_hostname_changed), view);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+
+ /* user name */
+ label = gtk_label_new("User name");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), priv->account->username().toLocal8Bit().constData());
+ g_signal_connect(entry, "changed", G_CALLBACK(account_username_changed), view);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+
+ /* password */
+ label = gtk_label_new("Password");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_input_purpose(GTK_ENTRY(entry), GTK_INPUT_PURPOSE_PASSWORD);
+ gtk_entry_set_icon_from_icon_name(GTK_ENTRY(entry), GTK_ENTRY_ICON_PRIMARY, "dialog-password");
+ gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+ gtk_entry_set_text(GTK_ENTRY(entry), priv->account->username().toLocal8Bit().constData());
+ g_signal_connect(entry, "changed", G_CALLBACK(account_password_changed), view);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+
+ /* show password */
+ checkbutton = gtk_check_button_new_with_label("Show password");
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), checkbutton, 1, grid_row, 1, 1);
+ g_signal_connect(checkbutton, "toggled", G_CALLBACK(show_password), entry);
+ ++grid_row;
+
+ /* proxy */
+ label = gtk_label_new("Proxy");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), priv->account->proxy().toLocal8Bit().constData());
+ g_signal_connect(entry, "changed", G_CALLBACK(account_proxy_changed), view);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+
+ /* voicemail number */
+ label = gtk_label_new("Voicemail number");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), priv->account->mailbox().toLocal8Bit().constData());
+ g_signal_connect(entry, "changed", G_CALLBACK(account_mailbox_changed), view);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+ } else {
+ /* RING accoutn */
+
+ /* bootstrap */
+ label = gtk_label_new("Bootstrap");
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), label, 0, grid_row, 1, 1);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), priv->account->hostname().toLocal8Bit().constData());
+ g_signal_connect(entry, "changed", G_CALLBACK(account_hostname_changed), view);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), entry, 1, grid_row, 1, 1);
+ ++grid_row;
+ }
+ }
+
+ /* auto answer */
+ checkbutton = gtk_check_button_new_with_label("Auto-answer calls");
+ gtk_widget_set_halign(checkbutton, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), checkbutton, 0, grid_row, 1, 1);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), priv->account->isAutoAnswer());
+ g_signal_connect(checkbutton, "toggled", G_CALLBACK(auto_answer), view);
+ ++grid_row;
+
+ /* upnp */
+ checkbutton = gtk_check_button_new_with_label("UPnP enabled");
+ gtk_widget_set_halign(checkbutton, GTK_ALIGN_START);
+ gtk_grid_attach(GTK_GRID(priv->grid_parameters), checkbutton, 0, grid_row, 1, 1);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), priv->account->isUpnpEnabled());
+ g_signal_connect(checkbutton, "toggled", G_CALLBACK(upnp_enabled), view);
+ ++grid_row;
+
+ gtk_widget_show_all(priv->grid_parameters);
+
+}
+
+GtkWidget *
+account_general_tab_new(Account *account)
+{
+ g_return_val_if_fail(account != NULL, NULL);
+
+ gpointer view = g_object_new(ACCOUNT_GENERAL_TAB_TYPE, NULL);
+
+ AccountGeneralTabPrivate *priv = ACCOUNT_GENERAL_TAB_GET_PRIVATE(view);
+ priv->account = account;
+
+ build_tab_view(ACCOUNT_GENERAL_TAB(view));
+
+ return (GtkWidget *)view;
+}
diff --git a/src/accountgeneraltab.h b/src/accountgeneraltab.h
new file mode 100644
index 0000000..c411315
--- /dev/null
+++ b/src/accountgeneraltab.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#ifndef _ACCOUNTGENERALTAB_H
+#define _ACCOUNTGENERALTAB_H
+
+#include <gtk/gtk.h>
+#include <account.h>
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_GENERAL_TAB_TYPE (account_general_tab_get_type ())
+#define ACCOUNT_GENERAL_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_GENERAL_TAB_TYPE, AccountGeneralTab))
+#define ACCOUNT_GENERAL_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_GENERAL_TAB_TYPE, AccountGeneralTabClass))
+#define IS_ACCOUNT_GENERAL_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_GENERAL_TAB_TYPE))
+#define IS_ACCOUNT_GENERAL_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_GENERAL_TAB_TYPE))
+
+typedef struct _AccountGeneralTab AccountGeneralTab;
+typedef struct _AccountGeneralTabClass AccountGeneralTabClass;
+
+GType account_general_tab_get_type (void) G_GNUC_CONST;
+GtkWidget *account_general_tab_new (Account *account);
+
+G_END_DECLS
+
+#endif /* _ACCOUNTGENERALTAB_H */
\ No newline at end of file
diff --git a/src/accountvideotab.cpp b/src/accountvideotab.cpp
new file mode 100644
index 0000000..57dfe4c
--- /dev/null
+++ b/src/accountvideotab.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#include "accountvideotab.h"
+
+#include <gtk/gtk.h>
+#include <account.h>
+#include <audio/codecmodel.h>
+#include "models/gtkqsortfiltertreemodel.h"
+
+struct _AccountVideoTab
+{
+ GtkBox parent;
+};
+
+struct _AccountVideoTabClass
+{
+ GtkBoxClass parent_class;
+};
+
+typedef struct _AccountVideoTabPrivate AccountVideoTabPrivate;
+
+struct _AccountVideoTabPrivate
+{
+ Account *account;
+ GtkWidget *treeview_codecs;
+ GtkWidget *checkbutton_enable;
+ GtkWidget *button_moveup;
+ GtkWidget *button_movedown;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountVideoTab, account_video_tab, GTK_TYPE_BOX);
+
+#define ACCOUNT_VIDEO_TAB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_VIDEO_TAB_TYPE, AccountVideoTabPrivate))
+
+static void
+account_video_tab_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(account_video_tab_parent_class)->dispose(object);
+}
+
+static void
+account_video_tab_init(AccountVideoTab *view)
+{
+ gtk_widget_init_template(GTK_WIDGET(view));
+}
+
+static void
+account_video_tab_class_init(AccountVideoTabClass *klass)
+{
+ G_OBJECT_CLASS(klass)->dispose = account_video_tab_dispose;
+
+ gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+ "/cx/ring/RingGnome/accountvideotab.ui");
+
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountVideoTab, treeview_codecs);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountVideoTab, checkbutton_enable);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountVideoTab, button_moveup);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountVideoTab, button_movedown);
+}
+
+static void
+video_enable(GtkToggleButton *checkbutton, AccountVideoTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_VIDEO_TAB(view));
+ AccountVideoTabPrivate *priv = ACCOUNT_VIDEO_TAB_GET_PRIVATE(view);
+ priv->account->setVideoEnabled(gtk_toggle_button_get_active(checkbutton));
+}
+
+static void
+codec_active_toggled(GtkCellRendererToggle *renderer, gchar *path, AccountVideoTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_VIDEO_TAB(view));
+ AccountVideoTabPrivate *priv = ACCOUNT_VIDEO_TAB_GET_PRIVATE(view);
+
+ /* we want to set it to the opposite of the current value */
+ gboolean toggle = !gtk_cell_renderer_toggle_get_active(renderer);
+
+ /* get iter which was clicked */
+ GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_codecs));
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter(model, &iter, tree_path);
+
+ /* get qmodelindex from iter and set the model data */
+ QModelIndex idx = gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(model), &iter);
+ if (idx.isValid()) {
+ priv->account->codecModel()->videoCodecs()->setData(idx, QVariant(toggle), Qt::CheckStateRole);
+ priv->account->codecModel()->save();
+ }
+}
+
+static QModelIndex
+get_index_from_selection(GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ return gtk_q_sort_filter_tree_model_get_source_idx(GTK_Q_SORT_FILTER_TREE_MODEL(model), &iter);
+ } else {
+ return QModelIndex();
+ }
+}
+
+static void
+move_selected_codec(AccountVideoTab *view, int position_diff)
+{
+ g_return_if_fail(IS_ACCOUNT_VIDEO_TAB(view));
+ AccountVideoTabPrivate *priv = ACCOUNT_VIDEO_TAB_GET_PRIVATE(view);
+
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_codecs));
+ QModelIndex idx = get_index_from_selection(selection);
+
+ if(!idx.isValid())
+ return;
+
+ QMimeData* mime = priv->account->codecModel()->videoCodecs()->mimeData(QModelIndexList() << idx);
+ priv->account->codecModel()->videoCodecs()->dropMimeData(
+ mime,
+ Qt::MoveAction,
+ idx.row() + position_diff,
+ 0,
+ QModelIndex());
+ priv->account->saveCodecs();
+
+ /* now make sure to select the same codec which was moved
+ * TODO: UGLY! this should be somehow done in the qt modle bindings,
+ * or via a selection model, not here
+ */
+ int new_row = idx.row() + position_diff;
+ int row_count = priv->account->codecModel()->videoCodecs()->rowCount(idx.parent());
+ if (new_row < 0)
+ new_row = row_count - 1;
+ else if (new_row >= row_count)
+ new_row = 0;
+
+ idx = idx.sibling(new_row, idx.column());
+ GtkTreeIter iter;
+ if (gtk_q_sort_filter_tree_model_source_index_to_iter(
+ GTK_Q_SORT_FILTER_TREE_MODEL(gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_codecs))),
+ idx,
+ &iter)) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+}
+
+static void
+move_codec_up(G_GNUC_UNUSED GtkButton *button, AccountVideoTab *view)
+{
+ move_selected_codec(view, -1);
+}
+
+static void
+move_codec_down(G_GNUC_UNUSED GtkButton *button, AccountVideoTab *view)
+{
+ move_selected_codec(view, +1);
+}
+
+static void
+build_tab_view(AccountVideoTab *view)
+{
+ g_return_if_fail(IS_ACCOUNT_VIDEO_TAB(view));
+ AccountVideoTabPrivate *priv = ACCOUNT_VIDEO_TAB_GET_PRIVATE(view);
+
+ /* codec model */
+ GtkQSortFilterTreeModel *codec_model;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ codec_model = gtk_q_sort_filter_tree_model_new(
+ priv->account->codecModel()->videoCodecs(),
+ 3,
+ Qt::CheckStateRole, G_TYPE_BOOLEAN,
+ CodecModel::Role::NAME, G_TYPE_STRING,
+ CodecModel::Role::BITRATE, G_TYPE_STRING);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_codecs), GTK_TREE_MODEL(codec_model));
+
+ renderer = gtk_cell_renderer_toggle_new();
+ column = gtk_tree_view_column_new_with_attributes("Enabled", renderer, "active", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_codecs), column);
+
+ g_signal_connect(renderer, "toggled", G_CALLBACK(codec_active_toggled), view);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", 1, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_codecs), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Bitrate", renderer, "text", 2, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_codecs), column);
+
+ /* enable video checkbutton */
+ gtk_toggle_button_set_active(
+ GTK_TOGGLE_BUTTON(priv->checkbutton_enable), priv->account->isVideoEnabled());
+ g_signal_connect(priv->checkbutton_enable, "toggled", G_CALLBACK(video_enable), view);
+
+ /* connect move codecs up/down signals */
+ g_signal_connect(priv->button_moveup, "clicked", G_CALLBACK(move_codec_up), view);
+ g_signal_connect(priv->button_movedown, "clicked", G_CALLBACK(move_codec_down), view);
+}
+
+GtkWidget *
+account_video_tab_new(Account *account)
+{
+ g_return_val_if_fail(account != NULL, NULL);
+
+ gpointer view = g_object_new(ACCOUNT_VIDEO_TAB_TYPE, NULL);
+
+ AccountVideoTabPrivate *priv = ACCOUNT_VIDEO_TAB_GET_PRIVATE(view);
+ priv->account = account;
+
+ build_tab_view(ACCOUNT_VIDEO_TAB(view));
+
+ return (GtkWidget *)view;
+}
diff --git a/src/accountvideotab.h b/src/accountvideotab.h
new file mode 100644
index 0000000..83116a9
--- /dev/null
+++ b/src/accountvideotab.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#ifndef _ACCOUNTVIDEOTAB_H
+#define _ACCOUNTVIDEOTAB_H
+
+#include <gtk/gtk.h>
+#include <account.h>
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_VIDEO_TAB_TYPE (account_video_tab_get_type ())
+#define ACCOUNT_VIDEO_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_VIDEO_TAB_TYPE, AccountVideoTab))
+#define ACCOUNT_VIDEO_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_VIDEO_TAB_TYPE, AccountVideoTabClass))
+#define IS_ACCOUNT_VIDEO_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_VIDEO_TAB_TYPE))
+#define IS_ACCOUNT_VIDEO_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_VIDEO_TAB_TYPE))
+
+typedef struct _AccountVideoTab AccountVideoTab;
+typedef struct _AccountVideoTabClass AccountVideoTabClass;
+
+GType account_video_tab_get_type (void) G_GNUC_CONST;
+GtkWidget *account_video_tab_new (Account *account);
+
+G_END_DECLS
+
+#endif /* _ACCOUNTVIDEOTAB_H */
\ No newline at end of file
diff --git a/src/accountview.cpp b/src/accountview.cpp
new file mode 100644
index 0000000..5219261
--- /dev/null
+++ b/src/accountview.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#include "accountview.h"
+
+#include <gtk/gtk.h>
+#include <accountmodel.h>
+#include <audio/codecmodel.h>
+#include "models/gtkqtreemodel.h"
+#include "accountgeneraltab.h"
+#include "accountaudiotab.h"
+#include "accountvideotab.h"
+
+struct _AccountView
+{
+ GtkBox parent;
+};
+
+struct _AccountViewClass
+{
+ GtkBoxClass parent_class;
+};
+
+typedef struct _AccountViewPrivate AccountViewPrivate;
+
+struct _AccountViewPrivate
+{
+ GtkWidget *treeview_account_list;
+ GtkWidget *stack_account;
+ GtkWidget *current_account_notebook;
+
+ gint current_page; /* keeps track of current notebook page displayed */
+
+ QMetaObject::Connection account_changed;
+ QMetaObject::Connection codecs_changed;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AccountView, account_view, GTK_TYPE_BOX);
+
+#define ACCOUNT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ACCOUNT_VIEW_TYPE, AccountViewPrivate))
+
+static void
+account_view_dispose(GObject *object)
+{
+ AccountView *view = ACCOUNT_VIEW(object);
+ AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+ QObject::disconnect(priv->account_changed);
+ QObject::disconnect(priv->codecs_changed);
+
+ G_OBJECT_CLASS(account_view_parent_class)->dispose(object);
+}
+
+static QModelIndex
+get_index_from_selection(GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = NULL;
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ return gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
+ } else {
+ return QModelIndex();
+ }
+}
+
+static void
+cancel_account_changes(G_GNUC_UNUSED GtkButton *button, AccountView *view)
+{
+ g_debug("cancel changes");
+
+ g_return_if_fail(IS_ACCOUNT_VIEW(view));
+ AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
+ QModelIndex account_idx = get_index_from_selection(selection);
+ g_return_if_fail(account_idx.isValid());
+
+
+ /* reload the current account view unselectin and re-selecting it */
+ GtkTreeIter iter;
+ gtk_q_tree_model_source_index_to_iter(
+ GTK_Q_TREE_MODEL(gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_account_list))),
+ account_idx,
+ &iter);
+
+ QObject::disconnect(priv->account_changed);
+ QObject::disconnect(priv->codecs_changed);
+ gtk_tree_selection_unselect_all(selection);
+
+ /* reload account */
+ Account *account = AccountModel::instance()->getAccountByModelIndex(account_idx);
+ account->performAction(Account::EditAction::CANCEL);
+ /* TODO: clear the codecModel changes once this method is added */
+ // account->codecModel()->cancel();
+
+ /* re-select same item */
+ gtk_tree_selection_select_iter(selection, &iter);
+}
+
+static void
+apply_account_changes(G_GNUC_UNUSED GtkButton *button, Account *account)
+{
+ account->performAction(Account::EditAction::SAVE);
+ /* TODO: save the codecModel changes once the method to clear them is added */
+ // account->codecModel()->save();
+}
+
+static void
+account_selection_changed(GtkTreeSelection *selection, AccountView *view)
+{
+ g_return_if_fail(IS_ACCOUNT_VIEW(view));
+ AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+ GtkWidget *old_account_view = gtk_stack_get_visible_child(GTK_STACK(priv->stack_account));
+
+ /* 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));
+
+ if (priv->current_page < 0)
+ priv->current_page = 0;
+
+ QModelIndex account_idx = get_index_from_selection(selection);
+ if (!account_idx.isValid()) {
+ /* it nothing is slected, simply display something empty */
+ GtkWidget *empty_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ 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;
+ } else {
+ Account *account = AccountModel::instance()->getAccountByModelIndex(account_idx);
+
+ /* build new account view */
+ GtkWidget *hbox_account = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
+
+ /* create account notebook */
+ priv->current_account_notebook = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(hbox_account), priv->current_account_notebook, TRUE, TRUE, 0);
+
+ /* customize account view based on account */
+ GtkWidget *general_tab = account_general_tab_new(account);
+ gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+ general_tab,
+ gtk_label_new("General"));
+ GtkWidget *audio_tab = account_audio_tab_new(account);
+ gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+ audio_tab,
+ gtk_label_new("Audio"));
+ GtkWidget *video_tab = account_video_tab_new(account);
+ gtk_notebook_append_page(GTK_NOTEBOOK(priv->current_account_notebook),
+ video_tab,
+ gtk_label_new("Video"));
+
+ /* 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);
+
+ /* button box with cancel and apply buttons */
+ GtkWidget *account_button_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
+ GtkWidget *account_change_cancel = gtk_button_new_with_label("Cancel");
+ GtkWidget *account_change_apply = gtk_button_new_with_label("Apply");
+
+ if (account->editState() != Account::EditState::MODIFIED) {
+ gtk_widget_set_sensitive(account_change_cancel, FALSE);
+ gtk_widget_set_sensitive(account_change_apply, FALSE);
+ }
+
+ gtk_box_pack_end(GTK_BOX(account_button_box), account_change_cancel, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(account_button_box), account_change_apply, FALSE, FALSE, 0);
+ gtk_widget_set_halign(account_button_box, GTK_ALIGN_END);
+ gtk_box_set_spacing(GTK_BOX(account_button_box), 10);
+ gtk_box_pack_start(GTK_BOX(hbox_account), account_button_box, FALSE, FALSE, 0);
+
+ /* connect signals to buttons */
+ QObject::disconnect(priv->account_changed);
+ priv->account_changed = QObject::connect(
+ account,
+ &Account::changed,
+ [=](Account* a) {
+ if (a->editState() == Account::EditState::MODIFIED) {
+ /* account has been modified, enable buttons */
+ gtk_widget_set_sensitive(account_change_cancel, TRUE);
+ gtk_widget_set_sensitive(account_change_apply, TRUE);
+ } else {
+ gtk_widget_set_sensitive(account_change_cancel, FALSE);
+ gtk_widget_set_sensitive(account_change_apply, FALSE);
+ }
+ }
+ );
+
+ /* add the following signal once the 'cancel' method is added to the contactModel */
+ // QObject::disconnect(priv->codecs_changed);
+ // priv->codecs_changed = QObject::connect(
+ // account->codecModel(),
+ // &CodecModel::dataChanged,
+ // [=]() {
+ // /* data changed in codec model, this probably happened because
+ // * codecs were (de)selected */
+ // gtk_widget_set_sensitive(account_change_cancel, TRUE);
+ // gtk_widget_set_sensitive(account_change_apply, TRUE);
+ // }
+ // );
+
+ g_signal_connect(account_change_cancel, "clicked", G_CALLBACK(cancel_account_changes), view);
+ g_signal_connect(account_change_apply, "clicked", G_CALLBACK(apply_account_changes), account);
+
+ gtk_widget_show_all(hbox_account);
+
+ /* 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);
+ }
+
+ /* remove the old account view */
+ if (old_account_view)
+ gtk_container_remove(GTK_CONTAINER(priv->stack_account), old_account_view);
+}
+
+static void
+account_active_toggled(GtkCellRendererToggle *renderer, gchar *path, AccountView *view)
+{
+ g_return_if_fail(IS_ACCOUNT_VIEW(view));
+ AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+ /* we want to set it to the opposite of the current value */
+ gboolean toggle = !gtk_cell_renderer_toggle_get_active(renderer);
+
+ /* get iter which was clicked */
+ GtkTreePath *tree_path = gtk_tree_path_new_from_string(path);
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview_account_list));
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter(model, &iter, tree_path);
+
+ /* get qmodelindex from iter and set the model data */
+ QModelIndex idx = gtk_q_tree_model_get_source_idx(GTK_Q_TREE_MODEL(model), &iter);
+ if (idx.isValid()) {
+ /* check if it is the IP2IP account, as we don't want to be able to disable it */
+ QVariant alias = idx.data(static_cast<int>(Account::Role::Alias));
+ if (strcmp(alias.value<QString>().toLocal8Bit().constData(), "IP2IP") != 0)
+ AccountModel::instance()->setData(idx, QVariant(toggle), Qt::CheckStateRole);
+ }
+}
+
+static void
+account_view_init(AccountView *view)
+{
+ gtk_widget_init_template(GTK_WIDGET(view));
+
+ AccountViewPrivate *priv = ACCOUNT_VIEW_GET_PRIVATE(view);
+
+ /* account model */
+ GtkQTreeModel *account_model;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ account_model = gtk_q_tree_model_new(AccountModel::instance(), 4,
+ Account::Role::Enabled, G_TYPE_BOOLEAN,
+ Account::Role::Alias, G_TYPE_STRING,
+ Account::Role::Proto, G_TYPE_INT,
+ Account::Role::RegistrationState, G_TYPE_STRING);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview_account_list), GTK_TREE_MODEL(account_model));
+
+ renderer = gtk_cell_renderer_toggle_new();
+ column = gtk_tree_view_column_new_with_attributes("Enabled", renderer, "active", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_account_list), column);
+
+ g_signal_connect(renderer, "toggled", G_CALLBACK(account_active_toggled), view);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Alias", renderer, "text", 1, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_account_list), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Status", renderer, "text", 3, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(priv->treeview_account_list), column);
+
+ /* connect to selection signal */
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview_account_list));
+ g_signal_connect(selection, "changed", G_CALLBACK(account_selection_changed), view);
+
+ /* add an empty box to the account stack initially, otherwise there will
+ * be no cool animation when the first account is selected */
+ GtkWidget *empty_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ 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);
+}
+
+static void
+account_view_class_init(AccountViewClass *klass)
+{
+ G_OBJECT_CLASS(klass)->dispose = account_view_dispose;
+
+ gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
+ "/cx/ring/RingGnome/accountview.ui");
+
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, treeview_account_list);
+ gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AccountView, stack_account);
+}
+
+GtkWidget *
+account_view_new(void)
+{
+ return (GtkWidget *)g_object_new(ACCOUNT_VIEW_TYPE, NULL);
+}
diff --git a/src/accountview.h b/src/accountview.h
new file mode 100644
index 0000000..2931f50
--- /dev/null
+++ b/src/accountview.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 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.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+#ifndef _ACCOUNTVIEW_H
+#define _ACCOUNTVIEW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ACCOUNT_VIEW_TYPE (account_view_get_type ())
+#define ACCOUNT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACCOUNT_VIEW_TYPE, AccountView))
+#define ACCOUNT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), ACCOUNT_VIEW_TYPE, AccountViewClass))
+#define IS_ACCOUNT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), ACCOUNT_VIEW_TYPE))
+#define IS_ACCOUNT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), ACCOUNT_VIEW_TYPE))
+
+typedef struct _AccountView AccountView;
+typedef struct _AccountViewClass AccountViewClass;
+
+GType account_view_get_type (void) G_GNUC_CONST;
+GtkWidget *account_view_new (void);
+
+G_END_DECLS
+
+#endif /* _ACCOUNTVIEW_H */
\ No newline at end of file
diff --git a/src/ringmainwindow.cpp b/src/ringmainwindow.cpp
index 102e68d..0454119 100644
--- a/src/ringmainwindow.cpp
+++ b/src/ringmainwindow.cpp
@@ -42,8 +42,10 @@
#include <contactmethod.h>
#include <QtCore/QSortFilterProxyModel>
#include "models/gtkqsortfiltertreemodel.h"
+#include "accountview.h"
#define DEFAULT_VIEW_NAME "placeholder"
+#define ACCOUNT_VIEW_NAME "account"
#define VIEW_CONTACTS "contacts"
#define VIEW_HISTORY "history"
#define VIEW_PRESENCE "presence"
@@ -72,6 +74,7 @@
GtkWidget *search_entry;
GtkWidget *stack_main_view;
GtkWidget *button_placecall;
+ GtkWidget *account_view;
};
G_DEFINE_TYPE_WITH_PRIVATE(RingMainWindow, ring_main_window, GTK_TYPE_APPLICATION_WINDOW);
@@ -229,6 +232,18 @@
}
static void
+show_account_view(G_GNUC_UNUSED GSimpleAction *action, G_GNUC_UNUSED GVariant *param, gpointer win)
+{
+ g_debug("show account view");
+ RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
+
+ /* clear call selection so we can re-select any ongoing calls */
+ CallModel::instance()->selectionModel()->clearCurrentIndex();
+
+ gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_main_view), ACCOUNT_VIEW_NAME);
+}
+
+static void
search_entry_placecall(G_GNUC_UNUSED GtkWidget *entry, gpointer win)
{
RingMainWindowPrivate *priv = RING_MAIN_WINDOW_GET_PRIVATE(RING_MAIN_WINDOW(win));
@@ -544,6 +559,10 @@
CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
}
);
+
+ /* init the account view */
+ priv->account_view = account_view_new();
+ gtk_stack_add_named(GTK_STACK(priv->stack_main_view), priv->account_view, ACCOUNT_VIEW_NAME);
}
static void
@@ -572,8 +591,21 @@
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), RingMainWindow, button_placecall);
}
+static const GActionEntry ring_actions[] =
+{
+ { "accountview", show_account_view, NULL, NULL, NULL, {0} },
+ /* TODO: add preferences view */
+ // { "accountview", show_preferences_view, NULL, NULL, NULL, {0} },
+};
+
GtkWidget *
ring_main_window_new (GtkApplication *app)
{
- return (GtkWidget *)g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
+ gpointer win = g_object_new(RING_MAIN_WINDOW_TYPE, "application", app, NULL);
+
+ /* map actions */
+ g_action_map_add_action_entries(
+ G_ACTION_MAP(app), ring_actions, G_N_ELEMENTS(ring_actions), win);
+
+ return (GtkWidget *)win;
}
diff --git a/ui/accountaudiotab.ui b/ui/accountaudiotab.ui
new file mode 100644
index 0000000..df99a48
--- /dev/null
+++ b/ui/accountaudiotab.ui
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <template class="AccountAudioTab" parent="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="vbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <property name="border_width">10</property>
+ <child>
+ <object class="GtkFrame" id="frame_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkBox" id="hbox_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">horizontal</property>
+ <property name="margin_left">10</property>
+ <property name="margin_start">10</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <property name="min-content-height">225</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButtonBox" id="vbox_codec_buttons">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <property name="valign">GTK_ALIGN_START</property>
+ <child>
+ <object class="GtkButton" id="button_moveup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Up</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_movedown">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Down</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes"><b>Codecs</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/ui/accountgeneraltab.ui b/ui/accountgeneraltab.ui
new file mode 100644
index 0000000..9466022
--- /dev/null
+++ b/ui/accountgeneraltab.ui
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <template class="AccountGeneralTab" parent="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="vbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <property name="border_width">10</property>
+ <child>
+ <object class="GtkFrame" id="frame_account">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment_account">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">10</property>
+ <property name="top_padding">10</property>
+ <child>
+ <object class="GtkGrid" id="grid_account">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">10</property>
+ <property name="column_spacing">10</property>
+ <!-- code generated account properties go here, base on account type -->
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_account">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes"><b>Account</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_parameters">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment_parameters">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">10</property>
+ <property name="top_padding">10</property>
+ <child>
+ <object class="GtkGrid" id="grid_parameters">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">10</property>
+ <property name="column_spacing">10</property>
+ <!-- code generated account properties go here, base on account type -->
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_parameters">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes"><b>Parameters</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/ui/accountvideotab.ui b/ui/accountvideotab.ui
new file mode 100644
index 0000000..bb9759d
--- /dev/null
+++ b/ui/accountvideotab.ui
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <template class="AccountVideoTab" parent="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="vbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <property name="border_width">10</property>
+ <child>
+ <object class="GtkFrame" id="frame_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkBox" id="hbox_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">horizontal</property>
+ <property name="margin_left">10</property>
+ <property name="margin_start">10</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <property name="min-content-height">225</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButtonBox" id="vbox_codec_buttons">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <property name="valign">GTK_ALIGN_START</property>
+ <child>
+ <object class="GtkButton" id="button_moveup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Up</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_movedown">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Down</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_codecs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes"><b>Codecs</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkbutton_enable">
+ <property name="label" translatable="yes">Enable Video</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/ui/accountview.ui b/ui/accountview.ui
new file mode 100644
index 0000000..8969226
--- /dev/null
+++ b/ui/accountview.ui
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.10"/>
+ <template class="AccountView" parent="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="hbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">5</property>
+ <!-- account list on left side of the box -->
+ <child>
+ <object class="GtkBox" id="vbox_account_list">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_account_list">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <property name="vscrollbar_policy">never</property>
+ <property name="hscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_account_list">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <!-- end of account list on left side of box -->
+ <!-- individual account view -->
+ <child>
+ <object class="GtkStack" id="stack_account">
+ <property name="visible">True</property>
+ <property name="transition-type">GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT</property>
+ <property name="transition-duration">300</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <!-- end individual account view -->
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/ui/ringgearsmenu.ui b/ui/ringgearsmenu.ui
index c6e3e88..2704e16 100644
--- a/ui/ringgearsmenu.ui
+++ b/ui/ringgearsmenu.ui
@@ -9,7 +9,7 @@
</item>
<item>
<attribute name="label" translatable="yes">_Account Settings</attribute>
- <attribute name="action">app.accountsettings</attribute>
+ <attribute name="action">app.accountview</attribute>
</item>
</section>
<section>
diff --git a/ui/ui.gresource.xml b/ui/ui.gresource.xml
index 42328e0..74141c6 100644
--- a/ui/ui.gresource.xml
+++ b/ui/ui.gresource.xml
@@ -5,5 +5,9 @@
<file preprocess="xml-stripblanks">ringgearsmenu.ui</file>
<file preprocess="xml-stripblanks">incomingcallview.ui</file>
<file preprocess="xml-stripblanks">currentcallview.ui</file>
+ <file preprocess="xml-stripblanks">accountview.ui</file>
+ <file preprocess="xml-stripblanks">accountgeneraltab.ui</file>
+ <file preprocess="xml-stripblanks">accountaudiotab.ui</file>
+ <file preprocess="xml-stripblanks">accountvideotab.ui</file>
</gresource>
</gresources>
\ No newline at end of file