From b547c344e57c5476c9fd7ceabe0e35790cecf06a Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Wed, 1 Aug 2018 12:51:55 +0200 Subject: [PATCH] Add CallsCallWindow --- src/calls-call-window.c | 393 +++++++++++++++++++++++++++++++++++++++ src/calls-call-window.h | 46 +++++ src/calls.gresources.xml | 1 + src/meson.build | 1 + src/ui/call-window.ui | 153 +++++++++++++++ 5 files changed, 594 insertions(+) create mode 100644 src/calls-call-window.c create mode 100644 src/calls-call-window.h create mode 100644 src/ui/call-window.ui diff --git a/src/calls-call-window.c b/src/calls-call-window.c new file mode 100644 index 0000000..36652e0 --- /dev/null +++ b/src/calls-call-window.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2018 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 . + * + * Authors: + * Bob Ham + * Adrien Plazas + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "calls-call-window.h" +#include "calls-origin.h" +#include "calls-call-holder.h" +#include "calls-call-selector-item.h" +#include "calls-new-call-box.h" +#include "util.h" + +#include +#include + +#define HANDY_USE_UNSTABLE_API +#include + + +struct _CallsCallWindow +{ + GtkApplicationWindow parent_instance; + + GListStore *call_holders; + CallsCallHolder *focus; + + GtkInfoBar *info; + GtkLabel *info_label; + + GtkStack *main_stack; + GtkStack *header_bar_stack; + GtkButton *show_calls; + GtkStack *call_stack; + GtkFlowBox *call_selector; +}; + +enum { + ORIGIN_STORE_COLUMN_NAME, + ORIGIN_STORE_COLUMN_ORIGIN +}; + +G_DEFINE_TYPE (CallsCallWindow, calls_call_window, GTK_TYPE_APPLICATION_WINDOW); + + +CallsCallWindow * +calls_call_window_new (GtkApplication *application) +{ + return g_object_new (CALLS_TYPE_CALL_WINDOW, + "application", application, + NULL); +} + + +static void +update_visibility (CallsCallWindow *self) +{ + guint calls = g_list_model_get_n_items (G_LIST_MODEL (self->call_holders)); + + gtk_widget_set_visible (GTK_WIDGET (self), calls > 0); + gtk_widget_set_sensitive (GTK_WIDGET (self->show_calls), calls > 1); + + if (calls == 0) + { + gtk_stack_set_visible_child_name (self->main_stack, "calls"); + } + else if (calls == 1) + { + gtk_stack_set_visible_child_name (self->main_stack, "active-call"); + } +} + + +static void +show_message (CallsCallWindow *self, + const gchar *text, + GtkMessageType type) +{ + gtk_info_bar_set_message_type (self->info, type); + gtk_label_set_text (self->info_label, text); + gtk_widget_show (GTK_WIDGET (self->info)); + gtk_widget_queue_allocate (GTK_WIDGET (self)); +} + + +static void +info_response_cb (GtkInfoBar *infobar, + gint response_id, + CallsCallWindow *self) +{ + gtk_widget_hide (GTK_WIDGET (self->info)); + gtk_widget_queue_allocate (GTK_WIDGET (self)); +} + + +static GtkWidget * +call_holders_create_widget_cb (CallsCallHolder *holder, + CallsCallWindow *self) +{ + return GTK_WIDGET (calls_call_holder_get_selector_item (holder)); +} + + +static void +new_call_submitted_cb (CallsCallWindow *self, + CallsOrigin *origin, + const gchar *number, + CallsNewCallBox *new_call_box) +{ + g_return_if_fail (CALLS_IS_CALL_WINDOW (self)); + + calls_origin_dial (origin, number); +} + + +typedef gboolean (*FindCallHolderFunc) (CallsCallHolder *holder, + gpointer user_data); + + +static gboolean +find_call_holder_by_call (CallsCallHolder *holder, + gpointer user_data) +{ + CallsCallData *data = calls_call_holder_get_data (holder); + + return calls_call_data_get_call (data) == user_data; +} + + +/** Search through the list of call holders, returning the total + number of items in the list, the position of the holder within the + list and a pointer to the holder itself. */ +static gboolean +find_call_holder (CallsCallWindow *self, + guint *n_itemsp, + guint *positionp, + CallsCallHolder **holderp, + FindCallHolderFunc predicate, + gpointer user_data) +{ + GListModel * const model = G_LIST_MODEL (self->call_holders); + const guint n_items = g_list_model_get_n_items (model); + guint position = 0; + CallsCallHolder *holder; + + for (position = 0; position < n_items; ++position) + { + holder = CALLS_CALL_HOLDER (g_list_model_get_item (model, position)); + + if (predicate (holder, user_data)) + { +#define out(var) \ + if (var##p) \ + { \ + *var##p = var ; \ + } + + out (n_items); + out (position); + out (holder); + +#undef out + + return TRUE; + } + } + + return FALSE; +} + + +static void +set_focus (CallsCallWindow *self, + CallsCallHolder *holder) +{ + if (!holder) + { + holder = g_list_model_get_item (G_LIST_MODEL (self->call_holders), 0); + + if (!holder) + { + /* No calls */ + self->focus = NULL; + return; + } + } + + self->focus = holder; + + gtk_stack_set_visible_child_name (self->main_stack, "active-call"); + gtk_stack_set_visible_child_name (self->header_bar_stack, "active-call"); + gtk_stack_set_visible_child + (self->call_stack, + GTK_WIDGET (calls_call_holder_get_display (holder))); +} + + +static void +show_calls_clicked_cb (GtkButton *button, + CallsCallWindow *self) +{ + /* FIXME Setting only one of them should be enough as the properties are binded. */ + gtk_stack_set_visible_child_name (self->main_stack, "calls"); + gtk_stack_set_visible_child_name (self->header_bar_stack, "calls"); +} + + +static void +call_selector_child_activated_cb (GtkFlowBox *box, + GtkFlowBoxChild *child, + CallsCallWindow *self) +{ + GtkWidget *widget = gtk_bin_get_child (GTK_BIN (child)); + CallsCallSelectorItem *item = CALLS_CALL_SELECTOR_ITEM (widget); + CallsCallHolder *holder = calls_call_selector_item_get_holder (item); + + update_visibility (self); + set_focus (self, holder); +} + + +void +calls_call_window_add_call (CallsCallWindow *self, + CallsCall *call) +{ + CallsCallHolder *holder; + CallsCallDisplay *display; + + g_return_if_fail (CALLS_IS_CALL_WINDOW (self)); + g_return_if_fail (CALLS_IS_CALL (call)); + + g_signal_connect_swapped (call, "message", + G_CALLBACK (show_message), self); + + holder = calls_call_holder_new (call); + + display = calls_call_holder_get_display (holder); + gtk_stack_add_named (self->call_stack, GTK_WIDGET (display), + calls_call_get_number (call)); + + g_list_store_append (self->call_holders, holder); + + update_visibility (self); + set_focus (self, holder); +} + + +static void +remove_call_holder (CallsCallWindow *self, + guint n_items, + guint position, + CallsCallHolder *holder) +{ + g_list_store_remove (self->call_holders, position); + gtk_container_remove (GTK_CONTAINER (self->call_stack), + GTK_WIDGET (calls_call_holder_get_display (holder))); + + update_visibility (self); + if (self->focus == holder) + { + set_focus (self, NULL); + } +} + +void +calls_call_window_remove_call (CallsCallWindow *self, + CallsCall *call, + const gchar *reason) +{ + guint n_items, position; + CallsCallHolder *holder; + gboolean found; + + g_return_if_fail (CALLS_IS_CALL_WINDOW (self)); + g_return_if_fail (CALLS_IS_CALL (call)); + + found = find_call_holder (self, &n_items, &position, &holder, + find_call_holder_by_call, call); + g_return_if_fail (found); + + remove_call_holder (self, n_items, position, holder); + + if (!reason) + { + reason = "Call ended for unknown reason"; + } + show_message(self, reason, GTK_MESSAGE_INFO); +} + + +static void +remove_calls (CallsCallWindow *self) +{ + GList *children, *child; + + /* Safely remove the call stack's children. */ + children = gtk_container_get_children (GTK_CONTAINER (self->call_stack)); + for (child = children; child != NULL; child = child->next) + gtk_container_remove (GTK_CONTAINER (self->call_stack), + GTK_WIDGET (child->data)); + g_list_free (children); + + g_list_store_remove_all (self->call_holders); + + update_visibility (self); +} + + +static void +constructed (GObject *object) +{ + GObjectClass *parent_class = g_type_class_peek (GTK_TYPE_APPLICATION_WINDOW); + CallsCallWindow *self = CALLS_CALL_WINDOW (object); + + gtk_flow_box_bind_model (self->call_selector, + G_LIST_MODEL (self->call_holders), + (GtkFlowBoxCreateWidgetFunc) call_holders_create_widget_cb, + NULL, NULL); + + parent_class->constructed (object); +} + + +static void +calls_call_window_init (CallsCallWindow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->call_holders = g_list_store_new (CALLS_TYPE_CALL_HOLDER); + + update_visibility (self); +} + + +static void +dispose (GObject *object) +{ + GObjectClass *parent_class = g_type_class_peek (GTK_TYPE_APPLICATION_WINDOW); + CallsCallWindow *self = CALLS_CALL_WINDOW (object); + + if (self->call_holders) + { + remove_calls (self); + } + + g_clear_object (&self->call_holders); + + parent_class->dispose (object); +} + + +static void +calls_call_window_class_init (CallsCallWindowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->constructed = constructed; + object_class->dispose = dispose; + + gtk_widget_class_set_template_from_resource (widget_class, "/sm/puri/calls/ui/call-window.ui"); + gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, info); + gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, info_label); + gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, main_stack); + gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, header_bar_stack); + gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, show_calls); + gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, call_stack); + gtk_widget_class_bind_template_child (widget_class, CallsCallWindow, call_selector); + gtk_widget_class_bind_template_callback (widget_class, info_response_cb); + gtk_widget_class_bind_template_callback (widget_class, call_selector_child_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, show_calls_clicked_cb); + gtk_widget_class_bind_template_callback (widget_class, new_call_submitted_cb); +} diff --git a/src/calls-call-window.h b/src/calls-call-window.h new file mode 100644 index 0000000..695e008 --- /dev/null +++ b/src/calls-call-window.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 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 . + * + * Authors: + * Bob Ham + * Adrien Plazas + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef CALLS_CALL_WINDOW_H__ +#define CALLS_CALL_WINDOW_H__ + +#include + +#include "calls-call.h" + +G_BEGIN_DECLS + +#define CALLS_TYPE_CALL_WINDOW (calls_call_window_get_type ()) + +G_DECLARE_FINAL_TYPE (CallsCallWindow, calls_call_window, CALLS, CALL_WINDOW, GtkApplicationWindow); + +CallsCallWindow *calls_call_window_new (GtkApplication *application); +void calls_call_window_add_call (CallsCallWindow *self, CallsCall *call); +void calls_call_window_remove_call (CallsCallWindow *self, CallsCall *call, const gchar *reason); + +G_END_DECLS + +#endif /* CALLS_CALL_WINDOW_H__ */ diff --git a/src/calls.gresources.xml b/src/calls.gresources.xml index 355bfc8..59991dd 100644 --- a/src/calls.gresources.xml +++ b/src/calls.gresources.xml @@ -4,6 +4,7 @@ main-window.ui call-display.ui call-selector-item.ui + call-window.ui encryption-indicator.ui history-box.ui new-call-box.ui diff --git a/src/meson.build b/src/meson.build index 8c3d966..fffd99e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -45,6 +45,7 @@ calls_sources = ['calls-message-source.c', 'calls-message-source.h', 'calls-call-holder.c', 'calls-call-holder.h', 'calls-call-display.c', 'calls-call-display.h', 'calls-call-selector-item.c', 'calls-call-selector-item.h', + 'calls-call-window.c', 'calls-call-window.h', 'calls-encryption-indicator.c', 'calls-encryption-indicator.h', 'calls-history-box.c', 'calls-history-box.h', 'calls-new-call-box.c', 'calls-new-call-box.h', diff --git a/src/ui/call-window.ui b/src/ui/call-window.ui new file mode 100644 index 0000000..10c0f95 --- /dev/null +++ b/src/ui/call-window.ui @@ -0,0 +1,153 @@ + + + + + + +