add visual notification for unread messages

Change-Id: If9a5f9953711bef705bbe0951b6a6c3a2e59879b
Reviewed-by: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
diff --git a/src/conversationsview.cpp b/src/conversationsview.cpp
index 54504ca..0733fef 100644
--- a/src/conversationsview.cpp
+++ b/src/conversationsview.cpp
@@ -87,14 +87,14 @@
     {
         // Draw first contact.
         // NOTE: We just draw the first contact, must change this for conferences when they will have their own object
-        auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(row);
-        auto contact = priv->accountContainer_->info.contactModel->getContact(conversation.participants.front());
+        auto conversationInfo = priv->accountContainer_->info.conversationModel->filteredConversation(row);
+        auto contactInfo = priv->accountContainer_->info.contactModel->getContact(conversationInfo.participants.front());
         std::shared_ptr<GdkPixbuf> image;
         auto var_photo = GlobalInstances::pixmapManipulator().conversationPhoto(
-            conversation,
+            conversationInfo,
             priv->accountContainer_->info,
             QSize(50, 50),
-            contact.isPresent
+            contactInfo.isPresent
         );
         image = var_photo.value<std::shared_ptr<GdkPixbuf>>();
 
diff --git a/src/native/pixbufmanipulator.cpp b/src/native/pixbufmanipulator.cpp
index a57d646..04c392e 100644
--- a/src/native/pixbufmanipulator.cpp
+++ b/src/native/pixbufmanipulator.cpp
@@ -122,7 +122,11 @@
 }
 
 std::shared_ptr<GdkPixbuf>
-PixbufManipulator::scaleAndFrame(const GdkPixbuf *photo, const QSize& size, bool display_presence, bool is_present)
+PixbufManipulator::scaleAndFrame(const GdkPixbuf *photo,
+                                 const QSize& size,
+                                 bool display_presence,
+                                 bool is_present,
+                                 uint unreadMessages)
 {
     /**
      * for now, respect the height requested
@@ -158,6 +162,10 @@
     if (display_presence)
         result.reset(ring_draw_presence(result.get(), is_present), g_object_unref);
 
+    /* draw visual notification for unread messages */
+    if (unreadMessages)
+        result.reset(ring_draw_unread_messages(result.get(), unreadMessages), g_object_unref);
+
     return result;
 }
 
@@ -255,12 +263,12 @@
 }
 
 QVariant
-PixbufManipulator::conversationPhoto(const lrc::api::conversation::Info& conversation,
+PixbufManipulator::conversationPhoto(const lrc::api::conversation::Info& conversationInfo,
                                      const lrc::api::account::Info& accountInfo,
                                      const QSize& size,
                                      bool displayPresence)
 {
-    auto contacts = conversation.participants;
+    auto contacts = conversationInfo.participants;
     if (!contacts.empty())
     {
         // Get first contact photo
@@ -268,16 +276,17 @@
         auto contactInfo = accountInfo.contactModel->getContact(contactUri);
         auto contactPhoto = contactInfo.profileInfo.avatar;
         auto bestName = contactInfo.profileInfo.alias.empty()? contactInfo.registeredName : contactInfo.profileInfo.alias;
+        auto unreadMessages = conversationInfo.unreadMessages;
         if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY && contactInfo.profileInfo.uri.empty()) {
-            return QVariant::fromValue(scaleAndFrame(temporaryItemAvatar().get(), size, false, false));
+            return QVariant::fromValue(scaleAndFrame(temporaryItemAvatar().get(), size, false, false, unreadMessages));
         } else if (contactInfo.profileInfo.type == lrc::api::profile::Type::SIP) {
             return QVariant::fromValue(scaleAndFrame(generateAvatar(bestName, "").get(), size, displayPresence, contactInfo.isPresent));
         } else if (!contactPhoto.empty()) {
             QByteArray byteArray(contactPhoto.c_str(), contactPhoto.length());
             QVariant photo = personPhoto(byteArray);
-            return QVariant::fromValue(scaleAndFrame(photo.value<std::shared_ptr<GdkPixbuf>>().get(), size, displayPresence, contactInfo.isPresent));
+            return QVariant::fromValue(scaleAndFrame(photo.value<std::shared_ptr<GdkPixbuf>>().get(), size, displayPresence, contactInfo.isPresent, unreadMessages));
         } else {
-            return QVariant::fromValue(scaleAndFrame(generateAvatar(bestName, contactInfo.profileInfo.uri).get(), size, displayPresence, contactInfo.isPresent));
+            return QVariant::fromValue(scaleAndFrame(generateAvatar(bestName, contactInfo.profileInfo.uri).get(), size, displayPresence, contactInfo.isPresent, unreadMessages));
         }
     }
     // should not
diff --git a/src/native/pixbufmanipulator.h b/src/native/pixbufmanipulator.h
index 3aa6a65..0760e02 100644
--- a/src/native/pixbufmanipulator.h
+++ b/src/native/pixbufmanipulator.h
@@ -68,7 +68,7 @@
     std::shared_ptr<GdkPixbuf> generateAvatar(const ContactMethod* cm) const;
     std::shared_ptr<GdkPixbuf> generateAvatar(const std::string& alias, const std::string& uri) const;
 
-    std::shared_ptr<GdkPixbuf> scaleAndFrame(const GdkPixbuf *photo, const QSize& size, bool display_presence = false, bool is_present = false);
+    std::shared_ptr<GdkPixbuf> scaleAndFrame(const GdkPixbuf *photo, const QSize& size, bool display_presence = false, bool is_present = false, uint unreadMessages = 0);
     std::shared_ptr<GdkPixbuf> conferenceAvatar_;
 };
 
diff --git a/src/utils/drawing.cpp b/src/utils/drawing.cpp
index f4bd4f8..99e653b 100644
--- a/src/utils/drawing.cpp
+++ b/src/utils/drawing.cpp
@@ -26,7 +26,7 @@
 static constexpr const char* MSG_COUNT_FONT        = "Sans";
 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     MSG_COUNT_BACKGROUND  = {0.984, 0.282, 0.278, 1.0}; // red 251, 72, 71, 1.0
 static constexpr GdkRGBA     PRESENCE_PRESENT_BACKGROUND = {0, 0.4156827, 0.8, 1.0}; // green 112, 217, 6, 0.9
 static constexpr GdkRGBA     PRESENCE_ABSENT_BACKGROUND = {0.984, 0.282, 0.278, 1.0}; // red 251, 72, 71, 0.9
 // This is the color palette for default avatars
@@ -263,7 +263,7 @@
     cairo_paint(cr);
 
     /* make text */
-    char *text = g_strdup_printf("%d", unread_count);
+    char *text = g_strdup_printf("%s", unread_count > 9 ? "9+" : std::to_string(unread_count).c_str());
     cairo_text_extents_t extents;
 
     cairo_select_font_face (cr, MSG_COUNT_FONT,
@@ -277,16 +277,18 @@
      * ie: 6 pixels higher, 6 pixels wider */
     int border_width = 3;
     double rec_x = w - extents.width - border_width * 2;
-    double rec_y = h - extents.height - border_width * 2;
+    double rec_y = 0;
     double rec_w = extents.width + border_width * 2;
     double rec_h = extents.height + 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);
-    cairo_set_source_rgba(cr, MSG_COUNT_BACKGROUND.red, MSG_COUNT_BACKGROUND.blue, MSG_COUNT_BACKGROUND.green, MSG_COUNT_BACKGROUND.alpha);
+    cairo_set_source_rgba(cr,
+                          MSG_COUNT_BACKGROUND.red,
+                          MSG_COUNT_BACKGROUND.blue, MSG_COUNT_BACKGROUND.green, MSG_COUNT_BACKGROUND.alpha);
     cairo_fill(cr);
 
     /* draw text */
-    cairo_move_to (cr, w-extents.width-border_width, h-border_width);
+    cairo_move_to (cr, w - extents.width-border_width, extents.height + border_width );
     cairo_set_source_rgb(cr, MSG_COUNT_FONT_COLOUR.red, MSG_COUNT_FONT_COLOUR.blue, MSG_COUNT_FONT_COLOUR.green);
     cairo_show_text (cr, text);