diff --git a/src/calls-account-overview.c b/src/calls-account-overview.c new file mode 100644 index 0000000..5ebe7b2 --- /dev/null +++ b/src/calls-account-overview.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2021 Purism SPC + * + * This file is part of Calls. + * + * Calls 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. + * + * Calls 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 Calls. If not, see . + * + * Author: Evangelos Ribeiro Tzaras + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#define G_LOG_DOMAIN "CallsAccountOverview" + +#include "calls-account.h" +#include "calls-account-overview.h" +#include "calls-account-row.h" +#include "calls-account-provider.h" + + +/** + * Section:calls-account-overview + * short_description: A #HdyWindow to manage VoIP accounts + * @Title: CallsAccountOverview + * + * This is a #HdyWindow derived window to display and manage the + * VoIP accounts. Each available #CallsAccount from any #CallsAccountProvider + * will be listed as a #CallsAccountRow. + */ + +typedef enum { + SHOW_INTRO = 0, + SHOW_OVERVIEW, +} CallsAccountOverviewState; + + +struct _CallsAccountOverview { + HdyWindow parent; + + /* UI widgets */ + GtkStack *stack; + GtkWidget *intro; + GtkWidget *overview; + GtkWidget *add_btn; + GtkWidget *add_row; + + /* The window where we add the account providers widget */ + GtkWindow *account_window; + GtkWidget *current_account_widget; + + /* misc */ + CallsAccountOverviewState state; + GList *providers; +}; + +G_DEFINE_TYPE (CallsAccountOverview, calls_account_overview, HDY_TYPE_WINDOW) + + +static void +update_visibility (CallsAccountOverview *self) +{ + g_assert (CALLS_IS_ACCOUNT_OVERVIEW (self)); + + switch (self->state) { + case SHOW_INTRO: + gtk_stack_set_visible_child (self->stack, self->intro); + break; + + case SHOW_OVERVIEW: + gtk_stack_set_visible_child (self->stack, self->overview); + break; + + default: + g_warn_if_reached (); + } +} + + +static void +update_state (CallsAccountOverview *self) +{ + guint n_origins = 0; + + g_assert (CALLS_IS_ACCOUNT_OVERVIEW (self)); + + for (GList *node = self->providers; node != NULL; node = node->next) { + CallsProvider *provider = CALLS_PROVIDER (node->data); + GListModel *model = calls_provider_get_origins (provider); + + n_origins += g_list_model_get_n_items (model); + } + + if (n_origins > 0) + self->state = SHOW_OVERVIEW; + else + self->state = SHOW_INTRO; + + update_visibility (self); +} + + +static void +attach_account_widget (CallsAccountOverview *self, + GtkWidget *widget) +{ + g_assert (CALLS_IS_ACCOUNT_OVERVIEW (self)); + g_assert (!widget || GTK_IS_WIDGET (widget)); + + if (widget == self->current_account_widget) + return; + + if (self->current_account_widget) + gtk_container_remove (GTK_CONTAINER (self->account_window), + self->current_account_widget); + + self->current_account_widget = widget; + if (widget) + gtk_container_add (GTK_CONTAINER (self->account_window), widget); +} + + +static void +on_add_account_clicked (CallsAccountOverview *self) +{ + CallsAccountProvider *provider; + GtkWidget *widget; + + /* For now we only have a single AccountProvider */ + provider = CALLS_ACCOUNT_PROVIDER (self->providers->data); + + widget = calls_account_provider_get_account_widget (provider); + attach_account_widget (self, widget); + + calls_account_provider_add_new_account (provider); + + gtk_window_present (self->account_window); +} + + +static void +calls_account_overview_class_init (CallsAccountOverviewClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/account-overview.ui"); + + gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, add_btn); + gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, add_row); + + gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, stack); + gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, intro); + gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, overview); + + gtk_widget_class_bind_template_child (widget_class, CallsAccountOverview, account_window); + + gtk_widget_class_bind_template_callback (widget_class, on_add_account_clicked); +} + + +static void +calls_account_overview_init (CallsAccountOverview *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_insert (GTK_LIST_BOX (self->overview), + GTK_WIDGET (self->add_row), + -1); + gtk_window_set_transient_for (self->account_window, GTK_WINDOW (self)); + + update_state (self); +} + + +CallsAccountOverview * +calls_account_overview_new (void) +{ + return g_object_new (CALLS_TYPE_ACCOUNT_OVERVIEW, + NULL); +} diff --git a/src/calls-account-overview.h b/src/calls-account-overview.h new file mode 100644 index 0000000..236a221 --- /dev/null +++ b/src/calls-account-overview.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 Purism SPC + * + * This file is part of Calls. + * + * Calls 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. + * + * Calls 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 Calls. If not, see . + * + * Author: Evangelos Ribeiro Tzaras + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define CALLS_TYPE_ACCOUNT_OVERVIEW (calls_account_overview_get_type ()) + +G_DECLARE_FINAL_TYPE (CallsAccountOverview, calls_account_overview, CALLS, ACCOUNT_OVERVIEW, HdyWindow) + +CallsAccountOverview *calls_account_overview_new (void); + +G_END_DECLS diff --git a/src/calls-account-row.c b/src/calls-account-row.c new file mode 100644 index 0000000..1824c5c --- /dev/null +++ b/src/calls-account-row.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2021 Purism SPC + * + * This file is part of Calls. + * + * Calls 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. + * + * Calls 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 Calls. If not, see . + * + * Author: Evangelos Ribeiro Tzaras + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#define G_LOG_DOMAIN "CallsAccountRow" + +#include "calls-account-row.h" +#include "calls-account-provider.h" + + +/** + * Section:calls-account-row + * short_description: A #HdyActionRow for use in #CallsAccountOverview + * @Title: CallsAccountRow + * + * This is a #HdyActionRow derived widget representing a #CallsAccount + * for VoIP accounts (currently only SIP). + */ + + +enum { + PROP_0, + PROP_PROVIDER, + PROP_ACCOUNT, + PROP_ONLINE, + PROP_LAST_PROP +}; +static GParamSpec *props[PROP_LAST_PROP]; + +enum { + EDIT_CLICKED, + N_SIGNALS +}; +static guint signals[N_SIGNALS]; + +struct _CallsAccountRow { + HdyActionRow parent; + + CallsAccountProvider *provider; + CallsAccount *account; + gboolean online; + + /* UI elements */ + HdyAvatar *avatar; + GtkSwitch *online_switch; + GtkButton *edit_btn; +}; + +G_DEFINE_TYPE (CallsAccountRow, calls_account_row, HDY_TYPE_ACTION_ROW) + + +static void +on_account_state_changed (CallsAccountRow *self) +{ + CallsAccountState state = calls_account_get_state (self->account); + + gtk_switch_set_active (self->online_switch, state == CALLS_ACCOUNT_ONLINE); +} + +static void +on_edit_clicked (CallsAccountRow *self) +{ + /** CallsAccountOverview connects to this signal to show + * the window containing the account providers widget. + * See calls_account_provider_get_account_widget() + */ + g_signal_emit (self, signals[EDIT_CLICKED], 0, self->provider, self->account); +} + + +static void +on_online_switched (CallsAccountRow *self) +{ + gboolean online; + + g_assert (CALLS_IS_ACCOUNT_ROW (self)); + + online = gtk_switch_get_active (self->online_switch); + calls_account_go_online (self->account, online); +} + + +static void +calls_account_row_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + CallsAccountRow *self = CALLS_ACCOUNT_ROW (object); + + switch (property_id) { + case PROP_PROVIDER: + self->provider = g_value_get_object (value); + break; + + case PROP_ACCOUNT: + self->account = g_value_get_object (value); + g_object_bind_property (self->account, "name", + self, "title", + G_BINDING_SYNC_CREATE); + g_object_bind_property (self->account, "address", + self, "subtitle", + G_BINDING_SYNC_CREATE); + + g_signal_connect_object (self->account, "notify::account-state", + G_CALLBACK (on_account_state_changed), self, + G_CONNECT_SWAPPED); + on_account_state_changed (self); + break; + + case PROP_ONLINE: + calls_account_row_set_online (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +calls_account_row_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + CallsAccountRow *self = CALLS_ACCOUNT_ROW (object); + + switch (property_id) { + case PROP_ACCOUNT: + g_value_set_object (value, calls_account_row_get_account (self)); + break; + + case PROP_ONLINE: + g_value_set_boolean (value, calls_account_row_get_online (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +calls_account_row_class_init (CallsAccountRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->set_property = calls_account_row_set_property; + object_class->get_property = calls_account_row_get_property; + + signals[EDIT_CLICKED] = + g_signal_new ("edit-clicked", + CALLS_TYPE_ACCOUNT_ROW, + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, + 2, + CALLS_TYPE_ACCOUNT_PROVIDER, + CALLS_TYPE_ACCOUNT); + + props[PROP_PROVIDER] = + g_param_spec_object ("provider", + "Provider", + "The provider of the account this row represents", + CALLS_TYPE_ACCOUNT_PROVIDER, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + props[PROP_ACCOUNT] = + g_param_spec_object ("account", + "Account", + "The account this row represents", + CALLS_TYPE_ACCOUNT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + props[PROP_ONLINE] = + g_param_spec_boolean ("online", + "online", + "The state of the online switch", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_LAST_PROP, props); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Calls/ui/account-row.ui"); + gtk_widget_class_bind_template_child (widget_class, CallsAccountRow, avatar); + gtk_widget_class_bind_template_child (widget_class, CallsAccountRow, online_switch); + gtk_widget_class_bind_template_child (widget_class, CallsAccountRow, edit_btn); + + gtk_widget_class_bind_template_callback (widget_class, on_edit_clicked); + gtk_widget_class_bind_template_callback (widget_class, on_online_switched); +} + + +static void +calls_account_row_init (CallsAccountRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + + +CallsAccountRow * +calls_account_row_new (CallsAccountProvider *provider, + CallsAccount *account) +{ + g_return_val_if_fail (CALLS_IS_ACCOUNT (account), NULL); + + return g_object_new (CALLS_TYPE_ACCOUNT_ROW, + "provider", provider, + "account", account, + NULL); +} + + +gboolean +calls_account_row_get_online (CallsAccountRow *self) +{ + g_return_val_if_fail (CALLS_IS_ACCOUNT_ROW (self), FALSE); + + return gtk_switch_get_active (self->online_switch); +} + + +void +calls_account_row_set_online (CallsAccountRow *self, + gboolean online) +{ + g_return_if_fail (CALLS_IS_ACCOUNT_ROW (self)); + + if (online == gtk_switch_get_active (self->online_switch)) + return; + + gtk_switch_set_active (self->online_switch, online); +} + + +CallsAccount * +calls_account_row_get_account (CallsAccountRow *self) +{ + g_return_val_if_fail (CALLS_IS_ACCOUNT_ROW (self), NULL); + + return self->account; +} diff --git a/src/calls-account-row.h b/src/calls-account-row.h new file mode 100644 index 0000000..1ab6971 --- /dev/null +++ b/src/calls-account-row.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 Purism SPC + * + * This file is part of Calls. + * + * Calls 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. + * + * Calls 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 Calls. If not, see . + * + * Author: Evangelos Ribeiro Tzaras + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include "calls-account.h" +#include "calls-account-provider.h" + +#include + + +G_BEGIN_DECLS + +#define CALLS_TYPE_ACCOUNT_ROW (calls_account_row_get_type ()) + +G_DECLARE_FINAL_TYPE (CallsAccountRow, calls_account_row, CALLS, ACCOUNT_ROW, HdyActionRow) + +CallsAccountRow *calls_account_row_new (CallsAccountProvider *provider, + CallsAccount *account); +gboolean calls_account_row_get_online (CallsAccountRow *self); +void calls_account_row_set_online (CallsAccountRow *self, + gboolean online); +CallsAccount *calls_account_row_get_account (CallsAccountRow *self); + +G_END_DECLS diff --git a/src/calls-application.c b/src/calls-application.c index 54ad861..fcddb54 100644 --- a/src/calls-application.c +++ b/src/calls-application.c @@ -350,6 +350,15 @@ copy_number (GSimpleAction *action, g_debug ("Copied `%s' to clipboard", number); } +static void +show_accounts (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) +{ + CallsApplication *app = CALLS_APPLICATION (g_application_get_default ()); + calls_main_window_show_accounts_overview (app->main_window); +} + static void manager_state_changed_cb (GApplication *application) { @@ -366,6 +375,8 @@ static const GActionEntry actions[] = { "set-daemon", set_daemon_action, NULL }, { "dial", dial_action, "s" }, { "copy-number", copy_number, "s"}, + /* TODO About dialog { "about", show_about, NULL}, */ + { "accounts", show_accounts, NULL}, }; diff --git a/src/calls-main-window.c b/src/calls-main-window.c index b764547..8fff4e7 100644 --- a/src/calls-main-window.c +++ b/src/calls-main-window.c @@ -23,6 +23,7 @@ */ #include "calls-main-window.h" +#include "calls-account-overview.h" #include "calls-origin.h" #include "calls-ussd.h" #include "calls-call-selector-item.h" @@ -54,6 +55,7 @@ struct _CallsMainWindow GtkRevealer *permanent_error_revealer; GtkLabel *permanent_error_label; + CallsAccountOverview *account_overview; CallsNewCallBox *new_call; GtkDialog *ussd_dialog; @@ -426,6 +428,7 @@ dispose (GObject *object) CallsMainWindow *self = CALLS_MAIN_WINDOW (object); g_clear_object (&self->record_store); + g_clear_object (&self->account_overview); G_OBJECT_CLASS (calls_main_window_parent_class)->dispose (object); } @@ -531,3 +534,17 @@ calls_main_window_dial (CallsMainWindow *self, calls_new_call_box_dial (self->new_call, target); } } + +void +calls_main_window_show_accounts_overview (CallsMainWindow *self) +{ + g_return_if_fail (CALLS_IS_MAIN_WINDOW (self)); + + if (self->account_overview == NULL) { + self->account_overview = calls_account_overview_new (); + gtk_window_set_transient_for (GTK_WINDOW (self->account_overview), + GTK_WINDOW (self)); + } + + gtk_window_present (GTK_WINDOW (self->account_overview)); +} diff --git a/src/calls-main-window.h b/src/calls-main-window.h index a058ff4..b5f3085 100644 --- a/src/calls-main-window.h +++ b/src/calls-main-window.h @@ -33,10 +33,11 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (CallsMainWindow, calls_main_window, CALLS, MAIN_WINDOW, HdyApplicationWindow); -CallsMainWindow *calls_main_window_new (GtkApplication *application, - GListModel *record_store); -void calls_main_window_dial (CallsMainWindow *self, - const gchar *target); +CallsMainWindow *calls_main_window_new (GtkApplication *application, + GListModel *record_store); +void calls_main_window_dial (CallsMainWindow *self, + const gchar *target); +void calls_main_window_show_accounts_overview (CallsMainWindow *self); G_END_DECLS diff --git a/src/calls.gresources.xml b/src/calls.gresources.xml index 9a4e4ca..09a0cde 100644 --- a/src/calls.gresources.xml +++ b/src/calls.gresources.xml @@ -14,6 +14,8 @@ in-app-notification.ui contacts-row.ui contacts-box.ui + account-overview.ui + account-row.ui style.css diff --git a/src/meson.build b/src/meson.build index 7a15fc9..290b6e2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -108,6 +108,8 @@ calls_sources = files(['calls-message-source.c', 'calls-message-source.h', 'calls-contacts-row.c', 'calls-contacts-row.h', 'calls-account.c', 'calls-account.h', 'calls-account-provider.c', 'calls-account-provider.h', + 'calls-account-overview.c', 'calls-account-overview.h', + 'calls-account-row.c', 'calls-account-row.h', 'calls-settings.c', 'calls-settings.h', ]) + calls_generated_sources diff --git a/src/ui/account-overview.ui b/src/ui/account-overview.ui new file mode 100644 index 0000000..5361fb3 --- /dev/null +++ b/src/ui/account-overview.ui @@ -0,0 +1,99 @@ + + + + + + + + True + True + True + _Add Account + + + + + False + 380 + 660 + + + + diff --git a/src/ui/account-row.ui b/src/ui/account-row.ui new file mode 100644 index 0000000..9be9fa6 --- /dev/null +++ b/src/ui/account-row.ui @@ -0,0 +1,41 @@ + + + + + + diff --git a/src/ui/main-window.ui b/src/ui/main-window.ui index ae598c0..597c492 100644 --- a/src/ui/main-window.ui +++ b/src/ui/main-window.ui @@ -30,7 +30,7 @@ - False + True False menu_popover @@ -225,8 +225,8 @@ True - VoIP Accounts - + _VoIP Accounts + app.accounts @@ -237,20 +237,21 @@ - True - Keyboard shortcuts + False + _Keyboard shortcuts + + + + + False + _Help True - Help - - - - - True - About Calls + _About Calls + app.about