gnome: outgoing calls

Can now place outgoing calls. Can end outgoing
and in progress calls.

Refs #66962

Change-Id: Ic132533d120e9582e58954c947af4cafd793b6fa
diff --git a/src/incomingcallview.cpp b/src/incomingcallview.cpp
index ac64b84..f7c1edd 100644
--- a/src/incomingcallview.cpp
+++ b/src/incomingcallview.cpp
@@ -31,8 +31,9 @@
 #include "incomingcallview.h"
 
 #include <gtk/gtk.h>
-#include <math.h>
 #include <call.h>
+#include "utils/drawing.h"
+#include <callmodel.h>
 
 struct _IncomingCallView
 {
@@ -50,6 +51,12 @@
 {
     GtkWidget *image_incoming;
     GtkWidget *label_identity;
+    GtkWidget *label_status;
+    GtkWidget *button_accept_incoming;
+    GtkWidget *button_reject_incoming;
+    GtkWidget *button_end_call;
+
+    QMetaObject::Connection state_change_connection;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(IncomingCallView, incoming_call_view, GTK_TYPE_BOX);
@@ -57,6 +64,20 @@
 #define INCOMING_CALL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), INCOMING_CALL_VIEW_TYPE, IncomingCallViewPrivate))
 
 static void
+incoming_call_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_OBJECT_CLASS(incoming_call_view_parent_class)->dispose(object);
+}
+
+static void
 incoming_call_view_init(IncomingCallView *view)
 {
     gtk_widget_init_template(GTK_WIDGET(view));
@@ -65,92 +86,17 @@
 static void
 incoming_call_view_class_init(IncomingCallViewClass *klass)
 {
+    G_OBJECT_CLASS(klass)->dispose = incoming_call_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, image_incoming);
     gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_identity);
-}
-
-static GdkPixbuf *
-draw_fallback_avatar(int size) {
-    cairo_surface_t *surface;
-    cairo_t *cr;
-
-    surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size, size);
-    cr = cairo_create(surface);
-
-    cairo_pattern_t *linpat = cairo_pattern_create_linear(0, 0, 0, size);
-    cairo_pattern_add_color_stop_rgb(linpat, 0, 0.937, 0.937, 0.937);
-    cairo_pattern_add_color_stop_rgb(linpat, 1, 0.969, 0.969, 0.969);
-
-    cairo_set_source(cr, linpat);
-    cairo_paint(cr);
-
-    int avatar_size = size * 0.3;
-    GtkIconInfo *icon_info = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), "avatar-default-symbolic",
-                                                        avatar_size, GTK_ICON_LOOKUP_GENERIC_FALLBACK);
-    GdkPixbuf *pixbuf_icon = gtk_icon_info_load_icon(icon_info, NULL);
-    g_object_unref(icon_info);
-
-    if (pixbuf_icon != NULL) {
-        gdk_cairo_set_source_pixbuf(cr, pixbuf_icon, (size - avatar_size) / 2, (size - avatar_size) / 2);
-        g_object_unref(pixbuf_icon);
-        cairo_rectangle(cr, (size - avatar_size) / 2, (size - avatar_size) / 2, avatar_size, avatar_size);
-        cairo_fill(cr);
-    }
-
-    GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, size, size);
-
-    /* free resources */
-    cairo_destroy(cr);
-    cairo_surface_destroy(surface);
-
-    return pixbuf;
-}
-
-static GdkPixbuf *
-frame_avatar(GdkPixbuf *avatar) {
-    int extra_space = 10;
-    int offset = extra_space/2;
-    int w = gdk_pixbuf_get_width(avatar);
-    int h = gdk_pixbuf_get_height(avatar);
-    int w_surface = w + extra_space;
-    int h_surface = h + extra_space;
-    cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w_surface, h_surface);
-    cairo_t *cr = cairo_create(surface);
-
-    cairo_set_source_rgba(cr, 0, 0, 0, 0);
-    cairo_rectangle(cr, 0, 0, w_surface, h_surface);
-    cairo_fill(cr);
-
-    gdk_cairo_set_source_pixbuf(cr, avatar, offset, offset);
-
-    double aspect = (double)w/(double)h;
-    double corner_radius = 5;
-    double radius = corner_radius/aspect;
-    double degrees = M_PI / 180.0;
-
-    cairo_new_sub_path (cr);
-    cairo_arc (cr, offset + w - radius, offset + radius, radius, -90 * degrees, 0 * degrees);
-    cairo_arc (cr, offset + w - radius, offset + h - radius, radius, 0 * degrees, 90 * degrees);
-    cairo_arc (cr, offset + radius, offset + h - radius, radius, 90 * degrees, 180 * degrees);
-    cairo_arc (cr, offset + radius, offset + radius, radius, 180 * degrees, 270 * degrees);
-    cairo_close_path (cr);
-
-    cairo_fill_preserve(cr);
-
-    cairo_set_source_rgba (cr, 58.0/256.0, 191/256.0, 210/256.0, 1.0);
-    cairo_set_line_width (cr, 2.0);
-    cairo_stroke (cr);
-
-    GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, w_surface, h_surface);
-
-    /* free resources */
-    cairo_destroy(cr);
-    cairo_surface_destroy(surface);
-
-    return pixbuf;
+    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);
 }
 
 GtkWidget *
@@ -159,6 +105,98 @@
     return (GtkWidget *)g_object_new(INCOMING_CALL_VIEW_TYPE, NULL);
 }
 
+static void
+update_state(IncomingCallView *view, Call *call)
+{
+    IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
+
+    /* change state label */
+    Call::State state = call->state();
+
+    switch(state) {
+        case Call::State::INCOMING:
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Incoming...");
+            break;
+        case Call::State::RINGING:
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Ringing...");
+            break;
+        case Call::State::CURRENT:
+            /* note: shouldn't be displayed, as the view should change */
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "In progress.");
+            break;
+        case Call::State::DIALING:
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Dialing...");
+            break;
+        case Call::State::HOLD:
+            /* note: shouldn't be displayed, as the view should change */
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "On hold.");
+            break;
+        case Call::State::FAILURE:
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Failed.");
+            break;
+        case Call::State::BUSY:
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Busy.");
+            break;
+        case Call::State::TRANSFERRED:
+            /* note: shouldn't be displayed, as the view should change */
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Transfered.");
+            break;
+        case Call::State::TRANSF_HOLD:
+            /* note: shouldn't be displayed, as the view should change */
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Transfer hold.");
+            break;
+        case Call::State::OVER:
+            /* note: shouldn't be displayed, as the view should change */
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Over.");
+            break;
+        case Call::State::ERROR:
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Error.");
+            break;
+        case Call::State::CONFERENCE:
+            /* note: shouldn't be displayed, as the view should change */
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Conference.");
+            break;
+        case Call::State::CONFERENCE_HOLD:
+            /* note: shouldn't be displayed, as the view should change */
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Conference hold.");
+            break;
+        case Call::State::INITIALIZATION:
+            gtk_label_set_text(GTK_LABEL(priv->label_status), "Initialization...");
+            break;
+        case Call::State::COUNT__:
+        break;
+    }
+
+    /* 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::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:
+            gtk_widget_show(priv->button_end_call);
+            break;
+        case Call::State::COUNT__:
+            break;
+    }
+}
+
 void
 incoming_call_view_set_call_info(IncomingCallView *view, const QModelIndex& idx) {
     IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
@@ -167,12 +205,23 @@
     QByteArray ba_name = var.toString().toLocal8Bit();
 
     /* get image and frame it */
-    GdkPixbuf *avatar = draw_fallback_avatar(100);
-    GdkPixbuf *framed_avatar = frame_avatar(avatar);
+    GdkPixbuf *avatar = ring_draw_fallback_avatar(100);
+    GdkPixbuf *framed_avatar = ring_frame_avatar(avatar);
     g_object_unref(avatar);
 
     gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_incoming), framed_avatar);
     g_object_unref(framed_avatar);
 
     gtk_label_set_text(GTK_LABEL(priv->label_identity), ba_name.data());
+
+    /* change some things depending on call state */
+    Call *call = CallModel::instance()->getCall(idx);
+
+    update_state(view, call);
+
+    priv->state_change_connection = QObject::connect(
+        call,
+        &Call::stateChanged,
+        [=]() { update_state(view, call); }
+    );
 }