Rewrite instant messaging screen

- HyperLink activated - YAY
- No more bubble size problem
- Username and date are now in an italic footer
- Username and date display are now saved in user preferences
- Remove sender avatar from right message as it's kind of
unnecessary

* Need LRC patch connecting message engine stuff : 4059 *

Change-Id: Id1adb1e96d99eb02a2c1e85a066dbbdc3e87cdc0
Tuleap: #601
Tuleap: #290
diff --git a/callwidget.cpp b/callwidget.cpp
index 988453a..c8bca30 100644
--- a/callwidget.cpp
+++ b/callwidget.cpp
@@ -20,6 +20,7 @@
 #include "ui_callwidget.h"
 
 #include <QClipboard>
+#include <QDesktopServices>
 
 #include <memory>
 
@@ -53,6 +54,7 @@
 #include "smartlistdelegate.h"
 #include "imdelegate.h"
 #include "pixbufmanipulator.h"
+#include "settingskey.h"
 
 #include "profilemodel.h"
 #include "peerprofilecollection.h"
@@ -233,17 +235,20 @@
             QApplication::clipboard()->setText(text.value<QString>());
         }
     });
+    QSettings settings;
     auto displayDate = new QAction(tr("Display date"), this);
     displayDate->setCheckable(true);
+    displayDate->setChecked(settings.value(SettingsKey::imShowDate).toBool());
     ui->listMessageView->addAction(displayDate);
     auto displayAuthor = new QAction(tr("Display author"), this);
     displayAuthor->setCheckable(true);
+    displayAuthor->setChecked(settings.value(SettingsKey::imShowAuthor).toBool());
     ui->listMessageView->addAction(displayAuthor);
     auto lamdba = [=](){
-        int opts = 0;
-        displayAuthor->isChecked() ? opts |= ImDelegate::DisplayOptions::AUTHOR : opts;
-        displayDate->isChecked() ? opts |= ImDelegate::DisplayOptions::DATE : opts;
-        imDelegate_->setDisplayOptions(static_cast<ImDelegate::DisplayOptions>(opts));
+        QSettings settings;
+        settings.setValue(SettingsKey::imShowAuthor, displayAuthor->isChecked());
+        settings.setValue(SettingsKey::imShowDate, displayDate->isChecked());
+        emit imDelegate_->sizeHintChanged(QModelIndex());
     };
     connect(displayAuthor, &QAction::triggered, lamdba);
     connect(displayDate, &QAction::triggered, lamdba);
@@ -690,6 +695,15 @@
         ui->contactMethodComboBox->addItem(cm->uri());
     }
     slidePage(ui->messagingPage, true);
+    disconnect(imClickedConnection_);
+    imClickedConnection_ = connect(ui->listMessageView, &QListView::clicked, [this](const QModelIndex& index) {
+        auto urlList = index.data(static_cast<int>(Media::TextRecording::Role::LinkList)).value<QList<QUrl>>();
+        if (urlList.size() == 1)
+            QDesktopServices::openUrl(urlList.at(0));
+        else if (urlList.size()) {
+            //TODO Handle multiple url in one message
+        }
+    });
 }
 
 void
diff --git a/callwidget.h b/callwidget.h
index d9e769c..69318b7 100644
--- a/callwidget.h
+++ b/callwidget.h
@@ -109,6 +109,7 @@
     QMetaObject::Connection imConnection_;
     QMetaObject::Connection imVisibleConnection_;
     QMetaObject::Connection callChangedConnection_;
+    QMetaObject::Connection imClickedConnection_;
     QPropertyAnimation* pageAnim_;
     QMenu* shareMenu_;
 
diff --git a/imdelegate.cpp b/imdelegate.cpp
index f18cf1d..054f4fc 100644
--- a/imdelegate.cpp
+++ b/imdelegate.cpp
@@ -19,36 +19,41 @@
 #include "imdelegate.h"
 
 #include <QApplication>
+#include <QTextDocument>
+#include <QSettings>
+#include <QDateTime>
 
 #include "media/text.h"
 #include "media/textrecording.h"
 
 #include "ringthemeutils.h"
+#include "settingskey.h"
 
 ImDelegate::ImDelegate(QObject *parent)
-    : QStyledItemDelegate(parent), showDate_(false), showAuthor_(false)
-{}
-
-void ImDelegate::setDisplayOptions(ImDelegate::DisplayOptions opt)
+    : QStyledItemDelegate(parent)
 {
-    showAuthor_ = opt & DisplayOptions::AUTHOR;
-    showDate_ = opt & DisplayOptions::DATE;
-    emit sizeHintChanged(QModelIndex());
 }
 
 void
 ImDelegate::formatMsg(const QModelIndex& index, QString& msg) const
 {
-    if (showAuthor_) {
-        auto author = index.data(
+    QSettings settings;
+    QStringList meta;
+    if (settings.value(SettingsKey::imShowAuthor).toBool()) {
+        meta << index.data(
                     static_cast<int>(Media::TextRecording::Role::AuthorDisplayname)).toString();
-        msg = QString("(%1)\n%2").arg(author, msg);
     }
-    if (showDate_) {
-        auto formattedDate = index.data(
-                    static_cast<int>(Media::TextRecording::Role::FormattedDate)).toString();
-        msg = QString("%2\n%1").arg(formattedDate, msg);
+    if (settings.value(SettingsKey::imShowDate).toBool()) {
+        auto timeStamp = index.data(
+                    static_cast<int>(Media::TextRecording::Role::Timestamp)).value<uint>();
+        auto date = QDateTime::fromTime_t(timeStamp);
+        auto now = QDateTime::currentDateTime();
+        if (now.date() == date.date())
+            meta << date.time().toString();
+        else
+            meta << date.toString();
     }
+    msg = QString("%2<footer><i>%1</i></footer>").arg(meta.join(" - "), msg);
 }
 
 void
@@ -64,7 +69,7 @@
     painter->setFont(fontMsg_);
 
     if (index.isValid()) {
-        auto msg = index.data(Qt::DisplayRole).toString();
+        auto msg = index.data(static_cast<int>(Media::TextRecording::Role::FormattedHtml)).toString();
         opt.text.clear();
         QStyle* style = opt.widget ? opt.widget->style() : QApplication::style();
 
@@ -76,53 +81,73 @@
 
         QRect textRect = getBoundingRect(dir, msg, opt);
 
-        QRect bubbleRect(textRect.left() - padding_,
-                         textRect.top() - padding_,
-                         textRect.width() + 2 * padding_,
-                         textRect.height() + 2 * padding_ );
-
-        opt.decorationSize = iconSize_;
-        opt.decorationPosition = (dir == Qt::AlignRight ?
-                                      QStyleOptionViewItem::Right : QStyleOptionViewItem::Left);
-        opt.decorationAlignment = Qt::AlignTop | Qt::AlignHCenter;
+        if (dir == Qt::AlignLeft) {
+            opt.decorationSize = iconSize_;
+            opt.decorationPosition = (dir == Qt::AlignRight ?
+                                          QStyleOptionViewItem::Right : QStyleOptionViewItem::Left);
+            opt.decorationAlignment = Qt::AlignTop | Qt::AlignHCenter;
+        } else
+            opt.decorationSize = QSize();
         style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
 
         QPainterPath path;
-        path.addRoundedRect(bubbleRect, padding_, padding_);
+        path.addRoundedRect(textRect, padding_, padding_);
 
         if (dir == Qt::AlignRight) {
             painter->fillPath(path, RingTheme::blue_);
-            painter->setPen(Qt::white);
         }
         else {
             painter->fillPath(path, Qt::white);
-            painter->setPen(Qt::black);
         }
 
-        painter->drawText(textRect, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, msg);
+        painter->save();
+
+        QTextDocument document;
+        document.setDefaultFont(fontMsg_);
+
+        if (dir == Qt::AlignRight)
+            document.setDefaultStyleSheet("body { color : white; } i { opacity: 100; font-size : 11px; text-align : right; }");
+        else
+            document.setDefaultStyleSheet("body { color : black; } i { opacity: 100; font-size : 11px; text-align : right; }");
+
+        document.setHtml(msg);
+
+        auto textOptions = QTextOption(Qt::AlignLeft);
+        textOptions.setWrapMode(QTextOption::WrapMode::WordWrap);
+        document.setDefaultTextOption(textOptions);
+        document.setTextWidth(textRect.width());
+
+        painter->translate(textRect.topLeft());
+        document.drawContents(painter);
+        painter->restore();
     }
 }
 
-QRect ImDelegate::getBoundingRect(const Qt::AlignmentFlag& dir, const QString& msg, const QStyleOptionViewItem &option) const
+QRect ImDelegate::getBoundingRect(const Qt::AlignmentFlag& dir,
+                                  const QString& msg,
+                                  const QStyleOptionViewItem &option) const
 {
-    QFont textFont = option.font;
-    QFontMetrics textFontMetrics(textFont);
     QRect textRect;
 
-    if (dir == Qt::AlignRight) {
-        textRect = textFontMetrics.boundingRect(option.rect.left() + 2 * padding_,
-                                                option.rect.top() + 2 * padding_,
-                                                option.rect.width() - iconSize_.width() - 4 * padding_,
-                                                0,
-                                                dir|Qt::AlignTop|Qt::TextWordWrap,
-                                                msg);
+    QTextDocument txtDoc;
+    txtDoc.setDefaultFont(fontMsg_);
+    txtDoc.setHtml(msg);
+    auto textOptions = QTextOption(Qt::AlignLeft);
+    textOptions.setWrapMode(QTextOption::WrapMode::WordWrap);
+    txtDoc.setDefaultTextOption(textOptions);
+
+    if (dir == Qt::AlignLeft) {
+        txtDoc.setTextWidth(option.rect.width() - iconSize_.width() - padding_);
+        textRect.setRect(option.rect.left() + iconSize_.width() + padding_,
+                         option.rect.top() + padding_,
+                         txtDoc.idealWidth(),
+                         txtDoc.size().height());
     } else {
-        textRect = textFontMetrics.boundingRect(option.rect.left() + iconSize_.width() + 2 * padding_,
-                                                option.rect.top() + 2 * padding_,
-                                                option.rect.width() - iconSize_.width() - 4 * padding_ ,
-                                                0,
-                                                dir|Qt::AlignTop|Qt::TextWordWrap,
-                                                msg);
+        txtDoc.setTextWidth(option.rect.width() - padding_);
+        textRect.setRect(option.rect.right() - padding_ - txtDoc.idealWidth(),
+                         option.rect.top() + padding_,
+                         txtDoc.idealWidth(),
+                         txtDoc.size().height());
     }
     return textRect;
 }
@@ -134,7 +159,7 @@
     QStyleOptionViewItem opt = option;
     opt.font = fontMsg_;
 
-    QString msg = index.data(Qt::DisplayRole).toString();
+    QString msg = index.data(static_cast<int>(Media::TextRecording::Role::FormattedHtml)).toString();
 
     auto dir = index.data(
                 static_cast<int>(Media::TextRecording::Role::Direction))
@@ -145,11 +170,13 @@
 
     QRect boundingRect = getBoundingRect(dir, msg, opt);
 
-    QSize size(option.rect.width(), boundingRect.height() + padding_);
+    QSize size(option.rect.width(), boundingRect.height());
 
     /* Keep the minimum height needed. */
     if(size.height() < iconSize_.height())
-        size.setHeight(iconSize_.height() + padding_);
+        size.setHeight(iconSize_.height());
+
+    size.setHeight(size.height() + 2 * padding_);
 
     return size;
 }
diff --git a/imdelegate.h b/imdelegate.h
index 1fee78f..7a1ba98 100644
--- a/imdelegate.h
+++ b/imdelegate.h
@@ -31,14 +31,10 @@
         DATE
     };
 
-    void setDisplayOptions(DisplayOptions opt);
 protected:
     void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
     QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
 private:
-    bool showDate_;
-    bool showAuthor_;
-
     void formatMsg(const QModelIndex& index, QString& msg) const;
     QRect getBoundingRect(const Qt::AlignmentFlag& dir, const QString& msg, const QStyleOptionViewItem &option) const;
 
diff --git a/instantmessagingwidget.cpp b/instantmessagingwidget.cpp
index 5001401..5cc13af 100644
--- a/instantmessagingwidget.cpp
+++ b/instantmessagingwidget.cpp
@@ -30,6 +30,7 @@
 
 #include "imdelegate.h"
 #include "globalsystemtray.h"
+#include "settingskey.h"
 
 InstantMessagingWidget::InstantMessagingWidget(QWidget *parent) :
     QWidget(parent),
@@ -47,17 +48,20 @@
     connect(copyAction, &QAction::triggered, [=]() {
         copyToClipboard();
     });
+    QSettings settings;
     auto displayDate = new QAction(tr("Display date"), this);
     displayDate->setCheckable(true);
+    displayDate->setChecked(settings.value(SettingsKey::imShowDate).toBool());
     ui->listMessageView->addAction(displayDate);
     auto displayAuthor = new QAction(tr("Display author"), this);
     displayAuthor->setCheckable(true);
+    displayAuthor->setChecked(settings.value(SettingsKey::imShowAuthor).toBool());
     ui->listMessageView->addAction(displayAuthor);
     auto lamdba = [=](){
-        int opts = 0;
-        displayAuthor->isChecked() ? opts |= ImDelegate::DisplayOptions::AUTHOR : opts;
-        displayDate->isChecked() ? opts |= ImDelegate::DisplayOptions::DATE : opts;
-        imDelegate_->setDisplayOptions(static_cast<ImDelegate::DisplayOptions>(opts));
+        QSettings settings;
+        settings.setValue(SettingsKey::imShowAuthor, displayAuthor->isChecked());
+        settings.setValue(SettingsKey::imShowDate, displayDate->isChecked());
+        emit imDelegate_->sizeHintChanged(QModelIndex());
     };
     connect(displayAuthor, &QAction::triggered, lamdba);
     connect(displayDate, &QAction::triggered, lamdba);
diff --git a/ringthemeutils.h b/ringthemeutils.h
index 4363697..76bb6f3 100644
--- a/ringthemeutils.h
+++ b/ringthemeutils.h
@@ -21,6 +21,7 @@
 namespace RingTheme {
 
 static const QColor blue_ {"#3AC0D2"};
+static const QColor lightBlue_ {"#c1ebf0"};
 static const QColor lightGrey_ {242, 242, 242};
 static const QColor lightBlack_ {63, 63, 63};
 static const QColor grey_ {192, 192, 192};
diff --git a/settingskey.h b/settingskey.h
index 0620901..def8286 100644
--- a/settingskey.h
+++ b/settingskey.h
@@ -24,6 +24,8 @@
 constexpr static char autoAnswer[] = "autoAnswer";
 constexpr static char savedSize[] = "savedSize";
 constexpr static char savedPos[] = "savedPos";
+constexpr static char imShowAuthor[] = "imShowAuthor";
+constexpr static char imShowDate[] = "imShowDate";
 }
 
 #define accountAutoAnswer(A) (A+SettingsKey::autoAnswer)