| /* |
| * Copyright (C) 2015-2016 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. |
| */ |
| |
| #include "incomingcallview.h" |
| |
| #include <gtk/gtk.h> |
| #include <call.h> |
| #include "utils/drawing.h" |
| #include <callmodel.h> |
| #include <contactmethod.h> |
| #include <person.h> |
| #include <globalinstances.h> |
| #include "native/pixbufmanipulator.h" |
| #include <itemdataroles.h> |
| #include <numbercategory.h> |
| #include "chatview.h" |
| #include "utils/files.h" |
| |
| struct _IncomingCallView |
| { |
| GtkBox parent; |
| }; |
| |
| struct _IncomingCallViewClass |
| { |
| GtkBoxClass parent_class; |
| }; |
| |
| typedef struct _IncomingCallViewPrivate IncomingCallViewPrivate; |
| |
| struct _IncomingCallViewPrivate |
| { |
| GtkWidget *paned_call; |
| GtkWidget *image_incoming; |
| GtkWidget *label_name; |
| GtkWidget *label_bestId; |
| GtkWidget *spinner_status; |
| GtkWidget *placeholder; |
| GtkWidget *label_status; |
| GtkWidget *button_accept_incoming; |
| GtkWidget *button_reject_incoming; |
| GtkWidget *button_end_call; |
| GtkWidget *frame_chat; |
| |
| /* The webkit_chat_container is created once, then reused for all chat |
| * views */ |
| GtkWidget *webkit_chat_container; |
| |
| Call *call; |
| |
| QMetaObject::Connection state_change_connection; |
| |
| GSettings *settings; |
| }; |
| |
| G_DEFINE_TYPE_WITH_PRIVATE(IncomingCallView, incoming_call_view, GTK_TYPE_BOX); |
| |
| #define INCOMING_CALL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), INCOMING_CALL_VIEW_TYPE, IncomingCallViewPrivate)) |
| |
| static void |
| incoming_call_view_dispose(GObject *object) |
| { |
| IncomingCallView *view; |
| IncomingCallViewPrivate *priv; |
| |
| view = INCOMING_CALL_VIEW(object); |
| priv = INCOMING_CALL_VIEW_GET_PRIVATE(view); |
| |
| QObject::disconnect(priv->state_change_connection); |
| |
| g_clear_object(&priv->settings); |
| |
| G_OBJECT_CLASS(incoming_call_view_parent_class)->dispose(object); |
| } |
| |
| static gboolean |
| map_boolean_to_orientation(GValue *value, GVariant *variant, G_GNUC_UNUSED gpointer user_data) |
| { |
| if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) { |
| if (g_variant_get_boolean(variant)) { |
| // true, chat should be horizontal (to the right) |
| g_value_set_enum(value, GTK_ORIENTATION_HORIZONTAL); |
| } else { |
| // false, chat should be vertical (at the bottom) |
| g_value_set_enum(value, GTK_ORIENTATION_VERTICAL); |
| } |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static void |
| incoming_call_view_init(IncomingCallView *view) |
| { |
| gtk_widget_init_template(GTK_WIDGET(view)); |
| // gtk_widget_add_events(GTK_WIDGET(view), GDK_KEY_PRESS_MASK); |
| |
| auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(view); |
| |
| /* bind the chat orientation to the gsetting */ |
| priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL); |
| g_settings_bind_with_mapping(priv->settings, "chat-pane-horizontal", |
| priv->paned_call, "orientation", |
| G_SETTINGS_BIND_GET, |
| map_boolean_to_orientation, |
| nullptr, nullptr, nullptr); |
| } |
| |
| static void |
| incoming_call_view_class_init(IncomingCallViewClass *klass) |
| { |
| G_OBJECT_CLASS(klass)->dispose = incoming_call_view_dispose; |
| |
| gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass), |
| "/cx/ring/RingGnome/incomingcallview.ui"); |
| |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, paned_call); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, image_incoming); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_name); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_bestId); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, spinner_status); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, placeholder); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_status); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_accept_incoming); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_reject_incoming); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_end_call); |
| gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, frame_chat); |
| } |
| |
| static void |
| update_state(IncomingCallView *view, Call *call) |
| { |
| IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view); |
| |
| /* change state label */ |
| Call::State state = call->state(); |
| |
| gchar *status = g_strdup_printf("%s", call->toHumanStateName().toUtf8().constData()); |
| gtk_label_set_text(GTK_LABEL(priv->label_status), status); |
| g_free(status); |
| |
| /* change button(s) displayed */ |
| gtk_widget_hide(priv->button_accept_incoming); |
| gtk_widget_hide(priv->button_reject_incoming); |
| gtk_widget_hide(priv->button_end_call); |
| |
| switch(state) { |
| case Call::State::INCOMING: |
| gtk_widget_show(priv->button_accept_incoming); |
| gtk_widget_show(priv->button_reject_incoming); |
| break; |
| case Call::State::NEW: |
| case Call::State::ABORTED: |
| case Call::State::RINGING: |
| case Call::State::CURRENT: |
| case Call::State::DIALING: |
| case Call::State::HOLD: |
| case Call::State::FAILURE: |
| case Call::State::BUSY: |
| case Call::State::TRANSFERRED: |
| case Call::State::TRANSF_HOLD: |
| case Call::State::OVER: |
| case Call::State::ERROR: |
| case Call::State::CONFERENCE: |
| case Call::State::CONFERENCE_HOLD: |
| case Call::State::INITIALIZATION: |
| case Call::State::CONNECTED: |
| gtk_widget_show(priv->button_end_call); |
| break; |
| case Call::State::COUNT__: |
| break; |
| } |
| |
| if (call->lifeCycleState() == Call::LifeCycleState::INITIALIZATION) { |
| gtk_widget_show(priv->spinner_status); |
| gtk_widget_hide(priv->placeholder); |
| } else { |
| gtk_widget_show(priv->placeholder); |
| gtk_widget_hide(priv->spinner_status); |
| } |
| } |
| |
| static void |
| set_call_info(IncomingCallView *view, Call *call) { |
| IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view); |
| |
| priv->call = call; |
| |
| /* get call image */ |
| QVariant var_i = GlobalInstances::pixmapManipulator().callPhoto(call, QSize(110, 110), false); |
| std::shared_ptr<GdkPixbuf> image = var_i.value<std::shared_ptr<GdkPixbuf>>(); |
| gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_incoming), image.get()); |
| |
| /* get name */ |
| auto name = call->formattedName(); |
| gtk_label_set_text(GTK_LABEL(priv->label_name), name.toUtf8().constData()); |
| |
| /* get uri, if different from name */ |
| auto bestId = call->peerContactMethod()->getBestId(); |
| if (name != bestId) { |
| auto cat_bestId = g_strdup_printf("(%s) %s" |
| ,call->peerContactMethod()->category()->name().toUtf8().constData() |
| ,bestId.toUtf8().constData()); |
| gtk_label_set_text(GTK_LABEL(priv->label_bestId), cat_bestId); |
| g_free(cat_bestId); |
| gtk_widget_show(priv->label_bestId); |
| } |
| |
| /* change some things depending on call state */ |
| update_state(view, call); |
| |
| priv->state_change_connection = QObject::connect( |
| call, |
| &Call::stateChanged, |
| [=]() { update_state(view, call); } |
| ); |
| |
| /* show chat */ |
| auto chat_view = chat_view_new_cm(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), priv->call->peerContactMethod()); |
| gtk_widget_show(chat_view); |
| chat_view_set_header_visible(CHAT_VIEW(chat_view), FALSE); |
| gtk_container_add(GTK_CONTAINER(priv->frame_chat), chat_view); |
| } |
| |
| GtkWidget * |
| incoming_call_view_new(Call *call, WebKitChatContainer *webkit_chat_container) |
| { |
| auto self = g_object_new(INCOMING_CALL_VIEW_TYPE, NULL); |
| |
| IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(self); |
| priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container); |
| |
| set_call_info(INCOMING_CALL_VIEW(self), call); |
| |
| return GTK_WIDGET(self); |
| } |
| |
| Call* |
| incoming_call_view_get_call(IncomingCallView *self) |
| { |
| g_return_val_if_fail(IS_INCOMING_CALL_VIEW(self), nullptr); |
| auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(self); |
| |
| return priv->call; |
| } |