persons: improve caching

The caching implemented in the ImageManipulationDelegate is preventing
the change of anycontact photo at runtime (even the profile one).

This commit monitors the changes on a photo and invalidates the cache
when a changes is detected.
The caching system was also not safe to use because it was building
indexes out of possibily invalid persons.
The BrokerVC and PersonLinkerVC now use the DecorationRole to increase
photo quality.

Change-Id: I1a364bbb0ade130868014a10ec127eff8a7e620b
Tuleap: #697
diff --git a/src/BrokerVC.mm b/src/BrokerVC.mm
index ad99110..3c0bb3e 100644
--- a/src/BrokerVC.mm
+++ b/src/BrokerVC.mm
@@ -251,9 +251,7 @@
     NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
     [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];
     NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
-    Person* p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object));
-    QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(p, QSize(40,40));
-    [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
+    [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(qIdx.data(Qt::DecorationRole)))];
     return result;
 }
 
diff --git a/src/PersonLinkerVC.mm b/src/PersonLinkerVC.mm
index 707c88a..a17de8e 100644
--- a/src/PersonLinkerVC.mm
+++ b/src/PersonLinkerVC.mm
@@ -201,24 +201,8 @@
     NSTableCellView *result = [outlineView makeViewWithIdentifier:@"MainCell" owner:outlineView];
     NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
     NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
-
-
-    if (!qIdx.isValid()) {
-        [photoView setImage:nil];
-        [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];
-        return result;
-    }
-
-    if (auto p = qvariant_cast<Person*>(qIdx.data((int)Person::Role::Object))) {
-        QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(p, QSize(35,35));
-        [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
-    } else {
-        QVariant photo = GlobalInstances::pixmapManipulator().contactPhoto(nil, QSize(35,35));
-        [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(photo))];
-    }
-
+    [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(qIdx.data(Qt::DecorationRole)))];
     [displayName setStringValue:qIdx.data(Qt::DisplayRole).toString().toNSString()];
-
     return result;
 }
 
diff --git a/src/delegates/ImageManipulationDelegate.h b/src/delegates/ImageManipulationDelegate.h
index b99cd6a..f1c3957 100644
--- a/src/delegates/ImageManipulationDelegate.h
+++ b/src/delegates/ImageManipulationDelegate.h
@@ -21,6 +21,7 @@
 
 //Qt
 #import <QSize>
+#import <QPair>
 #import <QtGui/qpixmap.h>
 
 //Ring
@@ -63,7 +64,7 @@
         CGImageRef resizeCGImage(CGImageRef image, const QSize& size);
 
         QHash<QString, QPixmap> m_hDefaultUserPixmap;
-        QHash<QString, QPixmap> m_hContactsPixmap;
+        QHash<QString, QPair<QMetaObject::Connection, QPixmap>> m_hContactsPixmap;
 
         /**
          * Return a version of size destSize centered of the bigger photo
diff --git a/src/delegates/ImageManipulationDelegate.mm b/src/delegates/ImageManipulationDelegate.mm
index 082c941..4984d40 100644
--- a/src/delegates/ImageManipulationDelegate.mm
+++ b/src/delegates/ImageManipulationDelegate.mm
@@ -45,16 +45,17 @@
 
     QVariant ImageManipulationDelegate::contactPhoto(Person* c, const QSize& size, bool displayPresence) {
         const int radius = size.height() / 2;
-
-        auto index = QStringLiteral("%1%2%3").arg(size.width())
-                                            .arg(size.height())
-                                            .arg(QString::fromUtf8(c->uid()));
-        if (m_hContactsPixmap.contains(index)) {
-            return m_hContactsPixmap.value(index);
-        }
-
         QPixmap pxm;
         if (c && c->photo().isValid()) {
+            // Check cache
+            auto index = QStringLiteral("%1%2%3").arg(size.width())
+            .arg(size.height())
+            .arg(QString::fromUtf8(c->uid()));
+
+            if (m_hContactsPixmap.contains(index)) {
+                return m_hContactsPixmap.value(index).second;
+            }
+
             QPixmap contactPhoto(qvariant_cast<QPixmap>(c->photo()).scaled(size, Qt::KeepAspectRatioByExpanding,
                                                                            Qt::SmoothTransformation));
 
@@ -89,12 +90,28 @@
             painter.setPen               (Qt::black                         );
             painter.setCompositionMode   (QPainter::CompositionMode_SourceIn);
             painter.drawRoundedRect(0,0,pxm.height(),pxm.height(),radius,radius);
-        }
-        else {
-            pxm = drawDefaultUserPixmap(size);
-        }
 
-        m_hContactsPixmap.insert(index, pxm);
+            // Save in cache
+            QPair<QMetaObject::Connection, QPixmap> toInsert;
+            toInsert.first = QObject::connect(c,
+                                              &Person::changed,
+                                              [=]() {
+                                                  if (c) {
+                                                      auto index = QStringLiteral("%1%2%3").arg(size.width())
+                                                                                            .arg(size.height())
+                                                                                            .arg(QString::fromUtf8(c->uid()));
+                                                      if (m_hContactsPixmap.contains(index)) {
+                                                          QObject::disconnect(m_hContactsPixmap.value(index).first);
+                                                          m_hContactsPixmap.remove(index);
+                                                      }
+                                                  }
+                                              });
+            toInsert.second = pxm;
+            m_hContactsPixmap.insert(index, toInsert);
+
+        } else {
+            return drawDefaultUserPixmap(size);
+        }
 
         return pxm;
     }