display presence status

- Modify RecentContactsView so that it displays the number of unread
  messages as a number

- Modify PixbufManipulator so that it draws presence status if
  displayPresence parameter is set to true. It dispays presence status
  as a green circle in the corner of the avatar.

[SS: moved drawing call to PixbufManipulator from RecentContactsView]
[SS: fixed unread count being hardcoded to 10]

Tuleap: #1379
Change-Id: I1fda061d26f231e9d0bb82f044eac91ecdb74db8
Signed-off-by: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
diff --git a/src/native/pixbufmanipulator.cpp b/src/native/pixbufmanipulator.cpp
index 1e534bf..3790d58 100644
--- a/src/native/pixbufmanipulator.cpp
+++ b/src/native/pixbufmanipulator.cpp
@@ -36,7 +36,7 @@
 }
 
 std::shared_ptr<GdkPixbuf>
-PixbufManipulator::scaleAndFrame(const GdkPixbuf *photo, const QSize& size)
+PixbufManipulator::scaleAndFrame(const GdkPixbuf *photo, const QSize& size, bool display_presence, bool is_present)
 {
     /**
      * for now, respect the height requested
@@ -63,14 +63,25 @@
         g_object_unref};
 
     /* frame photo */
-    return {ring_frame_avatar(scaled_photo.get()), g_object_unref};
+    std::shared_ptr<GdkPixbuf> result {
+        ring_frame_avatar(scaled_photo.get()),
+        g_object_unref
+    };
+
+    /* draw presence */
+    if (display_presence)
+        result.reset(ring_draw_presence(result.get(), is_present), g_object_unref);
+
+    return result;
 }
 
 QVariant
 PixbufManipulator::callPhoto(Call* c, const QSize& size, bool displayPresence)
 {
-    if (c->type() == Call::Type::CONFERENCE)
-        return QVariant::fromValue(scaleAndFrame(conferenceAvatar_.get(), size));
+    if (c->type() == Call::Type::CONFERENCE) {
+        /* conferences are always "online" */
+        return QVariant::fromValue(scaleAndFrame(conferenceAvatar_.get(), size, displayPresence, TRUE));
+    }
     return callPhoto(c->peerContactMethod(), size, displayPresence);
 }
 
@@ -80,15 +91,13 @@
     if (n->contact()) {
         return contactPhoto(n->contact(), size, displayPresence);
     } else {
-        return QVariant::fromValue(scaleAndFrame(fallbackAvatar_.get(), size));
+        return QVariant::fromValue(scaleAndFrame(fallbackAvatar_.get(), size, displayPresence, n->isPresent()));
     }
 }
 
 QVariant
 PixbufManipulator::contactPhoto(Person* c, const QSize& size, bool displayPresence)
 {
-    Q_UNUSED(displayPresence);
-
     /**
      * try to get the photo
      * otherwise use the fallback avatar
@@ -101,7 +110,7 @@
     else
         photo = fallbackAvatar_;
 
-    return QVariant::fromValue(scaleAndFrame(photo.get(), size));
+    return QVariant::fromValue(scaleAndFrame(photo.get(), size, displayPresence, c->isPresent()));
 }
 
 QVariant PixbufManipulator::personPhoto(const QByteArray& data, const QString& type)
diff --git a/src/native/pixbufmanipulator.h b/src/native/pixbufmanipulator.h
index 3122678..ddeb4e1 100644
--- a/src/native/pixbufmanipulator.h
+++ b/src/native/pixbufmanipulator.h
@@ -55,7 +55,7 @@
     QVariant   decorationRole(const Account* p) override;
 
 private:
-    std::shared_ptr<GdkPixbuf> scaleAndFrame(const GdkPixbuf *photo, const QSize& size);
+    std::shared_ptr<GdkPixbuf> scaleAndFrame(const GdkPixbuf *photo, const QSize& size, bool display_presence = false, bool is_present = false);
     std::shared_ptr<GdkPixbuf> fallbackAvatar_;
     std::shared_ptr<GdkPixbuf> conferenceAvatar_;
 };
diff --git a/src/recentcontactsview.cpp b/src/recentcontactsview.cpp
index 2d14c77..f903fba 100644
--- a/src/recentcontactsview.cpp
+++ b/src/recentcontactsview.cpp
@@ -102,22 +102,18 @@
     if (idx.isValid() && object.isValid()) {
         QVariant var_photo;
         if (auto person = object.value<Person *>()) {
-            var_photo = GlobalInstances::pixmapManipulator().contactPhoto(person, QSize(50, 50), false);
+            var_photo = GlobalInstances::pixmapManipulator().contactPhoto(person, QSize(50, 50), true);
         } else if (auto cm = object.value<ContactMethod *>()) {
             /* get photo, note that this should in all cases be the fallback avatar, since there
              * shouldn't be a person associated with this contact method */
-            var_photo = GlobalInstances::pixmapManipulator().callPhoto(cm, QSize(50, 50), false);
+            var_photo = GlobalInstances::pixmapManipulator().callPhoto(cm, QSize(50, 50), true);
         } else if (auto call = object.value<Call *>()) {
             if (call->type() == Call::Type::CONFERENCE) {
-                var_photo = GlobalInstances::pixmapManipulator().callPhoto(call, QSize(50, 50), false);
+                var_photo = GlobalInstances::pixmapManipulator().callPhoto(call, QSize(50, 50), true);
             }
         }
         if (var_photo.isValid()) {
-            std::shared_ptr<GdkPixbuf> photo = var_photo.value<std::shared_ptr<GdkPixbuf>>();
-
-            auto unread = idx.data(static_cast<int>(Ring::Role::UnreadTextMessageCount));
-
-            image.reset(ring_draw_unread_messages(photo.get(), unread.toInt()), g_object_unref);
+            image = var_photo.value<std::shared_ptr<GdkPixbuf>>();
         } else {
             // set the width of the cell rendered to the with of the photo
             // so that the other renderers are shifted to the right
@@ -279,6 +275,13 @@
                 {
                     text = g_markup_escape_text(duration.value<QString>().toUtf8().constData(), -1);
                 }
+                else
+                {
+                    auto unread = idx.data(static_cast<int>(Ring::Role::UnreadTextMessageCount)).toInt();
+                    if (unread > 0){
+                        text = g_markup_printf_escaped("<span color=\"red\" font_weight=\"bold\">%d</span>", unread);
+                    }
+                }
             }
             break;
             case Ring::ObjectType::Call:
@@ -623,7 +626,7 @@
         self,
         NULL);
 
-    /* call duration */
+    /* call duration or unread messages */
     renderer = gtk_cell_renderer_text_new();
     g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
     gtk_cell_area_box_pack_end(GTK_CELL_AREA_BOX(area), renderer, FALSE, FALSE, FALSE);
diff --git a/src/utils/drawing.cpp b/src/utils/drawing.cpp
index eec274a..20d33c8 100644
--- a/src/utils/drawing.cpp
+++ b/src/utils/drawing.cpp
@@ -26,6 +26,8 @@
 static constexpr int         MSG_COUNT_FONT_SIZE   = 12;
 static constexpr GdkRGBA     MSG_COUNT_FONT_COLOUR = {1.0, 1.0, 1.0, 1.0}; // white
 static constexpr GdkRGBA     MSG_COUNT_BACKGROUND  = {0.984, 0.282, 0.278, 0.9}; // red 251, 72, 71, 0.9
+static constexpr GdkRGBA     PRESENCE_PRESENT_BACKGROUND = {0.4375, 0.0234375, 0.84765625, 0.9}; // green 112, 217, 6, 0.9
+static constexpr GdkRGBA     PRESENCE_ABSENT_BACKGROUND = {0.984, 0.282, 0.278, 0.9}; // red 251, 72, 71, 0.9
 
 GdkPixbuf *
 ring_draw_fallback_avatar(int size) {
@@ -170,6 +172,55 @@
 }
 
 /**
+ * Draws the presence icon in the top right corner of the given image.
+ */
+GdkPixbuf *
+ring_draw_presence(const GdkPixbuf *avatar, bool present) {
+    if (!present) {
+        // simply return a copy of the original pixbuf
+        return gdk_pixbuf_copy(avatar);
+    }
+
+    int w = gdk_pixbuf_get_width(avatar);
+    int h = gdk_pixbuf_get_height(avatar);
+    cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
+    cairo_t *cr = cairo_create(surface);
+    cairo_surface_destroy(surface);
+
+    /* draw original image */
+    gdk_cairo_set_source_pixbuf(cr, avatar, 0, 0);
+    cairo_paint(cr);
+
+    /* draw rounded rectangle, with 3 pixel border
+     * ie: 6 pixels higher, 6 pixels wider */
+    int border_width = 5;
+    double rec_x = w - border_width * 2.5;
+    double rec_y = h - border_width * 2.5;
+    double rec_w = border_width * 2;
+    double rec_h = border_width * 2;
+    double corner_radius = rec_h/2.5;
+    create_rounded_rectangle_path(cr, corner_radius, rec_x, rec_y, rec_w, rec_h);
+
+    // For now we don't draw the absent background.
+    auto background = present ? PRESENCE_PRESENT_BACKGROUND : PRESENCE_ABSENT_BACKGROUND;
+    cairo_set_source_rgba(
+        cr,
+        background.red,
+        background.blue,
+        background.green,
+        background.alpha
+    );
+    cairo_fill(cr);
+
+    GdkPixbuf *pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, w, h);
+
+    /* free resources */
+    cairo_destroy(cr);
+
+    return pixbuf;
+}
+
+/**
  * Draws the unread message count in the bottom right corner of the given image.
  * In the case that the count is less than or equal to 0, nothing is drawn.
  */
diff --git a/src/utils/drawing.h b/src/utils/drawing.h
index ee96cc3..cbae46f 100644
--- a/src/utils/drawing.h
+++ b/src/utils/drawing.h
@@ -30,4 +30,6 @@
 
 GdkPixbuf *ring_draw_unread_messages(const GdkPixbuf *avatar, int unread_count);
 
+GdkPixbuf *ring_draw_presence(const GdkPixbuf *avatar, bool present);
+
 #endif /* _DRAWING */