src: Yearly copyright bump
[quassel.git] / src / qtui / chatscene.cpp
index 07adaa0..535f131 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2016 by the Quassel Project                        *
+ *   Copyright (C) 2005-2020 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
+#include "chatscene.h"
+
+#include <utility>
+
 #include <QApplication>
 #include <QClipboard>
 #include <QDesktopServices>
 #include <QDrag>
 #include <QGraphicsSceneMouseEvent>
-#include <QIcon>
 #include <QMenu>
 #include <QMenuBar>
 #include <QMimeData>
 #include <QPersistentModelIndex>
 #include <QUrl>
 
-#ifdef HAVE_KDE4
-#  include <KMenuBar>
-#else
-#  include <QMenuBar>
-#endif
-
-#ifdef HAVE_WEBKIT
-#  include <QWebView>
+#ifdef HAVE_WEBENGINE
+#    include <QWebEngineView>
+#elif defined HAVE_WEBKIT
+#    include <QWebView>
 #endif
 
 #include "chatitem.h"
 #include "chatline.h"
 #include "chatlinemodelitem.h"
-#include "chatscene.h"
 #include "chatview.h"
+#include "chatviewsettings.h"
 #include "client.h"
 #include "clientbacklogmanager.h"
 #include "columnhandleitem.h"
 #include "contextmenuactionprovider.h"
+#include "icon.h"
 #include "mainwin.h"
 #include "markerlineitem.h"
 #include "messagefilter.h"
 #include "qtui.h"
 #include "qtuistyle.h"
-#include "chatviewsettings.h"
 #include "webpreviewitem.h"
 
 const qreal minContentsWidth = 200;
 
-ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal width, ChatView *parent)
-    : QGraphicsScene(0, 0, width, 0, (QObject *)parent),
-    _chatView(parent),
-    _idString(idString),
-    _model(model),
-    _singleBufferId(BufferId()),
-    _sceneRect(0, 0, width, 0),
-    _firstLineRow(-1),
-    _viewportHeight(0),
-    _markerLine(new MarkerLineItem(width)),
-    _markerLineVisible(false),
-    _markerLineValid(false),
-    _markerLineJumpPending(false),
-    _cutoffMode(CutoffRight),
-    _selectingItem(0),
-    _selectionStart(-1),
-    _isSelecting(false),
-    _clickMode(NoClick),
-    _clickHandled(true),
-    _leftButtonPressed(false)
+ChatScene::ChatScene(QAbstractItemModel* model, QString idString, qreal width, ChatView* parent)
+    : QGraphicsScene(0, 0, width, 0, (QObject*)parent)
+    , _chatView(parent)
+    , _idString(std::move(idString))
+    , _model(model)
+    , _singleBufferId(BufferId())
+    , _sceneRect(0, 0, width, 0)
+    , _firstLineRow(-1)
+    , _viewportHeight(0)
+    , _markerLine(new MarkerLineItem(width))
+    , _markerLineVisible(false)
+    , _markerLineValid(false)
+    , _markerLineJumpPending(false)
+    , _cutoffMode(CutoffRight)
+    , _alwaysBracketSender(false)
+    , _selectingItem(nullptr)
+    , _selectionStart(-1)
+    , _isSelecting(false)
+    , _clickMode(NoClick)
+    , _clickHandled(true)
+    , _leftButtonPressed(false)
 {
-    MessageFilter *filter = qobject_cast<MessageFilter *>(model);
+    auto* filter = qobject_cast<MessageFilter*>(model);
     if (filter && filter->isSingleBufferFilter()) {
         _singleBufferId = filter->singleBufferId();
     }
 
     addItem(_markerLine);
-    connect(this, SIGNAL(sceneRectChanged(const QRectF &)), _markerLine, SLOT(sceneRectChanged(const QRectF &)));
+    connect(this, &QGraphicsScene::sceneRectChanged, _markerLine, &MarkerLineItem::sceneRectChanged);
 
     ChatViewSettings defaultSettings;
     _defaultFirstColHandlePos = defaultSettings.value("FirstColumnHandlePos", 80).toInt();
@@ -99,88 +99,85 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w
     _firstColHandle = new ColumnHandleItem(QtUi::style()->firstColumnSeparator());
     addItem(_firstColHandle);
     _firstColHandle->setXPos(_firstColHandlePos);
-    connect(_firstColHandle, SIGNAL(positionChanged(qreal)), this, SLOT(firstHandlePositionChanged(qreal)));
-    connect(this, SIGNAL(sceneRectChanged(const QRectF &)), _firstColHandle, SLOT(sceneRectChanged(const QRectF &)));
+    connect(_firstColHandle, &ColumnHandleItem::positionChanged, this, &ChatScene::firstHandlePositionChanged);
+    connect(this, &QGraphicsScene::sceneRectChanged, _firstColHandle, &ColumnHandleItem::sceneRectChanged);
 
     _secondColHandle = new ColumnHandleItem(QtUi::style()->secondColumnSeparator());
     addItem(_secondColHandle);
     _secondColHandle->setXPos(_secondColHandlePos);
-    connect(_secondColHandle, SIGNAL(positionChanged(qreal)), this, SLOT(secondHandlePositionChanged(qreal)));
+    connect(_secondColHandle, &ColumnHandleItem::positionChanged, this, &ChatScene::secondHandlePositionChanged);
 
-    connect(this, SIGNAL(sceneRectChanged(const QRectF &)), _secondColHandle, SLOT(sceneRectChanged(const QRectF &)));
+    connect(this, &QGraphicsScene::sceneRectChanged, _secondColHandle, &ColumnHandleItem::sceneRectChanged);
 
     setHandleXLimits();
 
     if (model->rowCount() > 0)
         rowsInserted(QModelIndex(), 0, model->rowCount() - 1);
 
-    connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
-        this, SLOT(rowsInserted(const QModelIndex &, int, int)));
-    connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
-        this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int)));
-    connect(model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
-        this, SLOT(rowsRemoved()));
-    connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(dataChanged(QModelIndex, QModelIndex)));
+    connect(model, &QAbstractItemModel::rowsInserted, this, &ChatScene::rowsInserted);
+    connect(model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ChatScene::rowsAboutToBeRemoved);
+    connect(model, &QAbstractItemModel::rowsRemoved, this, &ChatScene::rowsRemoved);
+    connect(model, &QAbstractItemModel::dataChanged, this, &ChatScene::dataChanged);
 
-#ifdef HAVE_WEBKIT
+#if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
     webPreview.timer.setSingleShot(true);
-    connect(&webPreview.timer, SIGNAL(timeout()), this, SLOT(webPreviewNextStep()));
+    connect(&webPreview.timer, &QTimer::timeout, this, &ChatScene::webPreviewNextStep);
 #endif
     _showWebPreview = defaultSettings.showWebPreview();
-    defaultSettings.notify("ShowWebPreview", this, SLOT(showWebPreviewChanged()));
+    defaultSettings.notify("ShowWebPreview", this, &ChatScene::showWebPreviewChanged);
+
+    _showSenderBrackets = defaultSettings.showSenderBrackets();
+    defaultSettings.notify("ShowSenderBrackets", this, &ChatScene::showSenderBracketsChanged);
+
+    _useCustomTimestampFormat = defaultSettings.useCustomTimestampFormat();
+    defaultSettings.notify("UseCustomTimestampFormat", this, &ChatScene::useCustomTimestampFormatChanged);
+
+    _timestampFormatString = defaultSettings.timestampFormatString();
+    defaultSettings.notify("TimestampFormat", this, &ChatScene::timestampFormatStringChanged);
+    updateTimestampHasBrackets();
 
     _clickTimer.setInterval(QApplication::doubleClickInterval());
     _clickTimer.setSingleShot(true);
-    connect(&_clickTimer, SIGNAL(timeout()), SLOT(clickTimeout()));
+    connect(&_clickTimer, &QTimer::timeout, this, &ChatScene::clickTimeout);
 
     setItemIndexMethod(QGraphicsScene::NoIndex);
 }
 
-
-ChatScene::~ChatScene()
-{
-}
-
-
-ChatView *ChatScene::chatView() const
+ChatView* ChatScene::chatView() const
 {
     return _chatView;
 }
 
-
-ColumnHandleItem *ChatScene::firstColumnHandle() const
+ColumnHandleItem* ChatScene::firstColumnHandle() const
 {
     return _firstColHandle;
 }
 
-
-ColumnHandleItem *ChatScene::secondColumnHandle() const
+ColumnHandleItem* ChatScene::secondColumnHandle() const
 {
     return _secondColHandle;
 }
 
 void ChatScene::resetColumnWidths()
 {
-    //make sure first column is at least 80 px wide, second 120 px
-    int firstColHandlePos = qMax(_defaultFirstColHandlePos,
-                                 80);
-    int secondColHandlePos = qMax(_defaultSecondColHandlePos,
-                                  firstColHandlePos + 120);
+    // make sure first column is at least 80 px wide, second 120 px
+    int firstColHandlePos = qMax(_defaultFirstColHandlePos, 80);
+    int secondColHandlePos = qMax(_defaultSecondColHandlePos, firstColHandlePos + 120);
 
     _firstColHandle->setXPos(firstColHandlePos);
     _secondColHandle->setXPos(secondColHandlePos);
 }
 
-ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact, bool ignoreDayChange) const
+ChatLineChatScene::chatLine(MsgId msgId, bool matchExact, bool ignoreDayChange) const
 {
     if (!_lines.count())
-        return 0;
+        return nullptr;
 
-    QList<ChatLine *>::ConstIterator start = _lines.begin();
-    QList<ChatLine *>::ConstIterator end = _lines.end();
-    QList<ChatLine *>::ConstIterator middle;
+    QList<ChatLine*>::ConstIterator start = _lines.begin();
+    QList<ChatLine*>::ConstIterator end = _lines.end();
+    QList<ChatLine*>::ConstIterator middle;
 
-    int n = int(end - start);
+    auto n = int(end - start);
     int half;
 
     while (n > 0) {
@@ -199,22 +196,22 @@ ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact, bool ignoreDayChange
         return *start;
 
     if (matchExact)
-        return 0;
+        return nullptr;
 
-    if (start == _lines.begin()) // not (yet?) in our scene
-        return 0;
+    if (start == _lines.begin())  // not (yet?) in our scene
+        return nullptr;
 
     // if we didn't find the exact msgId, take the next-lower one (this makes sense for lastSeen)
 
-    if (start == end) { // higher than last element
+    if (start == end) {  // higher than last element
         if (!ignoreDayChange)
             return _lines.last();
 
-        for (int i = _lines.count() -1; i >= 0; i--) {
+        for (int i = _lines.count() - 1; i >= 0; i--) {
             if (_lines.at(i)->msgType() != Message::DayChange)
                 return _lines.at(i);
         }
-        return 0;
+        return nullptr;
     }
 
     // return the next-lower line
@@ -224,33 +221,29 @@ ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact, bool ignoreDayChange
     do {
         if ((*(--start))->msgType() != Message::DayChange)
             return *start;
-    }
-    while (start != _lines.begin());
-    return 0;
+    } while (start != _lines.begin());
+    return nullptr;
 }
 
-
-ChatItem *ChatScene::chatItemAt(const QPointF &scenePos) const
+ChatItem* ChatScene::chatItemAt(const QPointF& scenePos) const
 {
-    foreach(QGraphicsItem *item, items(scenePos, Qt::IntersectsItemBoundingRect, Qt::AscendingOrder)) {
-        ChatLine *line = qgraphicsitem_cast<ChatLine *>(item);
+    foreach (QGraphicsItem* item, items(scenePos, Qt::IntersectsItemBoundingRect, Qt::AscendingOrder)) {
+        auto* line = qgraphicsitem_cast<ChatLine*>(item);
         if (line)
             return line->itemAt(line->mapFromScene(scenePos));
     }
-    return 0;
+    return nullptr;
 }
 
-
-bool ChatScene::containsBuffer(const BufferId &id) const
+bool ChatScene::containsBuffer(const BufferId& id) const
 {
-    MessageFilter *filter = qobject_cast<MessageFilter *>(model());
+    auto* filter = qobject_cast<MessageFilter*>(model());
     if (filter)
         return filter->containsBuffer(id);
     else
         return false;
 }
 
-
 void ChatScene::setMarkerLineVisible(bool visible)
 {
     _markerLineVisible = visible;
@@ -260,7 +253,6 @@ void ChatScene::setMarkerLineVisible(bool visible)
         markerLine()->setVisible(false);
 }
 
-
 void ChatScene::setMarkerLine(MsgId msgId)
 {
     if (!isSingleBufferScene())
@@ -270,7 +262,7 @@ void ChatScene::setMarkerLine(MsgId msgId)
         msgId = Client::markerLine(singleBufferId());
 
     if (msgId.isValid()) {
-        ChatLine *line = chatLine(msgId, false, true);
+        ChatLineline = chatLine(msgId, false, true);
         if (line) {
             markerLine()->setChatLine(line);
             // if this was the last line, we won't see it because it's outside the sceneRect
@@ -296,7 +288,6 @@ void ChatScene::setMarkerLine(MsgId msgId)
     markerLine()->setVisible(false);
 }
 
-
 void ChatScene::jumpToMarkerLine(bool requestBacklog)
 {
     if (!isSingleBufferScene())
@@ -321,28 +312,27 @@ void ChatScene::jumpToMarkerLine(bool requestBacklog)
     }
 }
 
-
-void ChatScene::rowsInserted(const QModelIndex &index, int start, int end)
+void ChatScene::rowsInserted(const QModelIndex& index, int start, int end)
 {
     Q_UNUSED(index);
 
-//   QModelIndex sidx = model()->index(start, 2);
-//   QModelIndex eidx = model()->index(end, 2);
-//   qDebug() << "rowsInserted:";
-//   if(start > 0) {
-//     QModelIndex ssidx = model()->index(start - 1, 2);
-//     qDebug() << "Start--:" << start - 1 << ssidx.data(MessageModel::MsgIdRole).value<MsgId>()
-//           << ssidx.data(Qt::DisplayRole).toString();
-//   }
-//   qDebug() << "Start:" << start << sidx.data(MessageModel::MsgIdRole).value<MsgId>()
-//         << sidx.data(Qt::DisplayRole).toString();
-//   qDebug() << "End:" << end << eidx.data(MessageModel::MsgIdRole).value<MsgId>()
-//         << eidx.data(Qt::DisplayRole).toString();
-//   if(end + 1 < model()->rowCount()) {
-//     QModelIndex eeidx = model()->index(end + 1, 2);
-//     qDebug() << "End++:" << end + 1 << eeidx.data(MessageModel::MsgIdRole).value<MsgId>()
-//           << eeidx.data(Qt::DisplayRole).toString();
-//   }
+    //   QModelIndex sidx = model()->index(start, 2);
+    //   QModelIndex eidx = model()->index(end, 2);
+    //   qDebug() << "rowsInserted:";
+    //   if(start > 0) {
+    //     QModelIndex ssidx = model()->index(start - 1, 2);
+    //     qDebug() << "Start--:" << start - 1 << ssidx.data(MessageModel::MsgIdRole).value<MsgId>()
+    //           << ssidx.data(Qt::DisplayRole).toString();
+    //   }
+    //   qDebug() << "Start:" << start << sidx.data(MessageModel::MsgIdRole).value<MsgId>()
+    //         << sidx.data(Qt::DisplayRole).toString();
+    //   qDebug() << "End:" << end << eidx.data(MessageModel::MsgIdRole).value<MsgId>()
+    //         << eidx.data(Qt::DisplayRole).toString();
+    //   if(end + 1 < model()->rowCount()) {
+    //     QModelIndex eeidx = model()->index(end + 1, 2);
+    //     qDebug() << "End++:" << end + 1 << eeidx.data(MessageModel::MsgIdRole).value<MsgId>()
+    //           << eeidx.data(Qt::DisplayRole).toString();
+    //   }
 
     qreal h = 0;
     qreal y = 0;
@@ -365,23 +355,17 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end)
 
     if (atTop) {
         for (int i = end; i >= start; i--) {
-            ChatLine *line = new ChatLine(i, model(),
-                width,
-                timestampWidth, senderWidth, contentsWidth,
-                senderPos, contentsPos);
+            auto* line = new ChatLine(i, model(), width, timestampWidth, senderWidth, contentsWidth, senderPos, contentsPos);
             h += line->height();
-            line->setPos(0, y-h);
+            line->setPos(0, y - h);
             _lines.insert(start, line);
             addItem(line);
         }
     }
     else {
         for (int i = start; i <= end; i++) {
-            ChatLine *line = new ChatLine(i, model(),
-                width,
-                timestampWidth, senderWidth, contentsWidth,
-                senderPos, contentsPos);
-            line->setPos(0, y+h);
+            auto* line = new ChatLine(i, model(), width, timestampWidth, senderWidth, contentsWidth, senderPos, contentsPos);
+            line->setPos(0, y + h);
             h += line->height();
             _lines.insert(i, line);
             addItem(line);
@@ -389,7 +373,7 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end)
     }
 
     // update existing items
-    for (int i = end+1; i < _lines.count(); i++) {
+    for (int i = end + 1; i < _lines.count(); i++) {
         _lines[i]->setRow(i);
     }
 
@@ -411,7 +395,7 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end)
 
     // neither pre- or append means we have to do dirty work: move items...
     if (!(atTop || atBottom)) {
-        ChatLine *line = 0;
+        ChatLine* line = nullptr;
         for (int i = 0; i <= end; i++) {
             line = _lines.at(i);
             line->setPos(0, line->pos().y() - h);
@@ -422,25 +406,23 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end)
 
     // check if all went right
     Q_ASSERT(start == 0 || _lines.at(start - 1)->pos().y() + _lines.at(start - 1)->height() == _lines.at(start)->pos().y());
-//   if(start != 0) {
-//     if(_lines.at(start - 1)->pos().y() + _lines.at(start - 1)->height() != _lines.at(start)->pos().y()) {
-//       qDebug() << "lines:" << _lines.count() << "start:" << start << "end:" << end;
-//       qDebug() << "line[start - 1]:" << _lines.at(start - 1)->pos().y() << "+" << _lines.at(start - 1)->height() << "=" << _lines.at(start - 1)->pos().y() + _lines.at(start - 1)->height();
-//       qDebug() << "line[start]" << _lines.at(start)->pos().y();
-//       qDebug() << "needed moving:" << !(atTop || atBottom) << moveTop << moveStart << moveEnd << offset;
-//       Q_ASSERT(false)
-//     }
-//   }
+    //   if(start != 0) {
+    //     if(_lines.at(start - 1)->pos().y() + _lines.at(start - 1)->height() != _lines.at(start)->pos().y()) {
+    //       qDebug() << "lines:" << _lines.count() << "start:" << start << "end:" << end;
+    //       qDebug() << "line[start - 1]:" << _lines.at(start - 1)->pos().y() << "+" << _lines.at(start - 1)->height() << "=" <<
+    //       _lines.at(start - 1)->pos().y() + _lines.at(start - 1)->height(); qDebug() << "line[start]" << _lines.at(start)->pos().y(); qDebug()
+    //       << "needed moving:" << !(atTop || atBottom) << moveTop << moveStart << moveEnd << offset; Q_ASSERT(false)
+    //     }
+    //   }
     Q_ASSERT(end + 1 == _lines.count() || _lines.at(end)->pos().y() + _lines.at(end)->height() == _lines.at(end + 1)->pos().y());
-//   if(end + 1 < _lines.count()) {
-//     if(_lines.at(end)->pos().y() + _lines.at(end)->height() != _lines.at(end + 1)->pos().y()) {
-//       qDebug() << "lines:" << _lines.count() << "start:" << start << "end:" << end;
-//       qDebug() << "line[end]:" << _lines.at(end)->pos().y() << "+" << _lines.at(end)->height() << "=" << _lines.at(end)->pos().y() + _lines.at(end)->height();
-//       qDebug() << "line[end+1]" << _lines.at(end + 1)->pos().y();
-//       qDebug() << "needed moving:" << !(atTop || atBottom) << moveTop << moveStart << moveEnd << offset;
-//       Q_ASSERT(false);
-//     }
-//   }
+    //   if(end + 1 < _lines.count()) {
+    //     if(_lines.at(end)->pos().y() + _lines.at(end)->height() != _lines.at(end + 1)->pos().y()) {
+    //       qDebug() << "lines:" << _lines.count() << "start:" << start << "end:" << end;
+    //       qDebug() << "line[end]:" << _lines.at(end)->pos().y() << "+" << _lines.at(end)->height() << "=" << _lines.at(end)->pos().y() +
+    //       _lines.at(end)->height(); qDebug() << "line[end+1]" << _lines.at(end + 1)->pos().y(); qDebug() << "needed moving:" << !(atTop
+    //       || atBottom) << moveTop << moveStart << moveEnd << offset; Q_ASSERT(false);
+    //     }
+    //   }
 
     if (!atBottom) {
         if (start < _firstLineRow) {
@@ -462,12 +444,11 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end)
         setMarkerLine();
 }
 
-
-void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+void ChatScene::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
 {
     Q_UNUSED(parent);
 
-    qreal h = 0; // total height of removed items;
+    qreal h = 0;  // total height of removed items;
 
     bool atTop = (start == 0);
     bool atBottom = (end == _lines.count() - 1);
@@ -476,15 +457,15 @@ void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e
     if (_selectingItem) {
         int row = _selectingItem->row();
         if (row >= start && row <= end)
-            setSelectingItem(0);
+            setSelectingItem(nullptr);
     }
 
     // remove items from scene
-    QList<ChatLine *>::iterator lineIter = _lines.begin() + start;
+    QList<ChatLine*>::iterator lineIter = _lines.begin() + start;
     int lineCount = start;
     while (lineIter != _lines.end() && lineCount <= end) {
         if ((*lineIter) == markerLine()->chatLine())
-            markerLine()->setChatLine(0);
+            markerLine()->setChatLine(nullptr);
         h += (*lineIter)->height();
         delete *lineIter;
         lineIter = _lines.erase(lineIter);
@@ -526,14 +507,15 @@ void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e
             moveStart = start;
             offset = -offset;
         }
-        ChatLine *line = 0;
+        ChatLine* line = nullptr;
         for (int i = moveStart; i <= moveEnd; i++) {
             line = _lines.at(i);
             line->setPos(0, line->pos().y() + offset);
         }
     }
 
-    Q_ASSERT(start == 0 || start >= _lines.count() || _lines.at(start - 1)->pos().y() + _lines.at(start - 1)->height() == _lines.at(start)->pos().y());
+    Q_ASSERT(start == 0 || start >= _lines.count()
+             || _lines.at(start - 1)->pos().y() + _lines.at(start - 1)->height() == _lines.at(start)->pos().y());
 
     // update sceneRect
     // when searching for the first non-date-line we have to take into account that our
@@ -549,50 +531,44 @@ void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e
             needOffset = true;
         }
         firstLineIdx = model()->index(_firstLineRow, 0);
-    }
-    while ((Message::Type)(model()->data(firstLineIdx, MessageModel::TypeRole).toInt()) == Message::DayChange && _firstLineRow < numRows);
+    } while ((Message::Type)(model()->data(firstLineIdx, MessageModel::TypeRole).toInt()) == Message::DayChange && _firstLineRow < numRows);
 
     if (needOffset)
         _firstLineRow -= end - start + 1;
     updateSceneRect();
 }
 
-
 void ChatScene::rowsRemoved()
 {
     // move the marker line if necessary
     setMarkerLine();
 }
 
-
-void ChatScene::dataChanged(const QModelIndex &tl, const QModelIndex &br)
+void ChatScene::dataChanged(const QModelIndex& tl, const QModelIndex& br)
 {
     layout(tl.row(), br.row(), _sceneRect.width());
 }
 
-
 void ChatScene::updateForViewport(qreal width, qreal height)
 {
     _viewportHeight = height;
     setWidth(width);
 }
 
-
 void ChatScene::setWidth(qreal width)
 {
     if (width == _sceneRect.width())
         return;
-    layout(0, _lines.count()-1, width);
+    layout(0, _lines.count() - 1, width);
 }
 
-
 void ChatScene::layout(int start, int end, qreal width)
 {
     // clock_t startT = clock();
 
     // disabling the index while doing this complex updates is about
     // 2 to 10 times faster!
-    //setItemIndexMethod(QGraphicsScene::NoIndex);
+    // setItemIndexMethod(QGraphicsScene::NoIndex);
 
     if (end >= 0) {
         int row = end;
@@ -604,7 +580,7 @@ void ChatScene::layout(int start, int end, qreal width)
 
         if (row >= 0) {
             // remaining items don't need geometry changes, but maybe repositioning?
-            ChatLine *line = _lines.at(row);
+            ChatLineline = _lines.at(row);
             qreal offset = linePos - (line->scenePos().y() + line->height());
             if (offset != 0) {
                 while (row >= 0) {
@@ -615,18 +591,17 @@ void ChatScene::layout(int start, int end, qreal width)
         }
     }
 
-    //setItemIndexMethod(QGraphicsScene::BspTreeIndex);
+    // setItemIndexMethod(QGraphicsScene::BspTreeIndex);
 
     updateSceneRect(width);
     setHandleXLimits();
     setMarkerLine();
     emit layoutChanged();
 
-//   clock_t endT = clock();
-//   qDebug() << "resized" << _lines.count() << "in" << (float)(endT - startT) / CLOCKS_PER_SEC << "sec";
+    //   clock_t endT = clock();
+    //   qDebug() << "resized" << _lines.count() << "in" << (float)(endT - startT) / CLOCKS_PER_SEC << "sec";
 }
 
-
 void ChatScene::firstHandlePositionChanged(qreal xpos)
 {
     if (_firstColHandlePos == xpos)
@@ -642,10 +617,10 @@ void ChatScene::firstHandlePositionChanged(qreal xpos)
 
     // disabling the index while doing this complex updates is about
     // 2 to 10 times faster!
-    //setItemIndexMethod(QGraphicsScene::NoIndex);
+    // setItemIndexMethod(QGraphicsScene::NoIndex);
 
-    QList<ChatLine *>::iterator lineIter = _lines.end();
-    QList<ChatLine *>::iterator lineIterBegin = _lines.begin();
+    QList<ChatLine*>::iterator lineIter = _lines.end();
+    QList<ChatLine*>::iterator lineIterBegin = _lines.begin();
     qreal timestampWidth = firstColumnHandle()->sceneLeft();
     qreal senderWidth = secondColumnHandle()->sceneLeft() - firstColumnHandle()->sceneRight();
     QPointF senderPos(firstColumnHandle()->sceneRight(), 0);
@@ -654,15 +629,14 @@ void ChatScene::firstHandlePositionChanged(qreal xpos)
         --lineIter;
         (*lineIter)->setFirstColumn(timestampWidth, senderWidth, senderPos);
     }
-    //setItemIndexMethod(QGraphicsScene::BspTreeIndex);
+    // setItemIndexMethod(QGraphicsScene::BspTreeIndex);
 
     setHandleXLimits();
 
-//   clock_t endT = clock();
-//   qDebug() << "resized" << _lines.count() << "in" << (float)(endT - startT) / CLOCKS_PER_SEC << "sec";
+    //   clock_t endT = clock();
+    //   qDebug() << "resized" << _lines.count() << "in" << (float)(endT - startT) / CLOCKS_PER_SEC << "sec";
 }
 
-
 void ChatScene::secondHandlePositionChanged(qreal xpos)
 {
     if (_secondColHandlePos == xpos)
@@ -678,10 +652,10 @@ void ChatScene::secondHandlePositionChanged(qreal xpos)
 
     // disabling the index while doing this complex updates is about
     // 2 to 10 times faster!
-    //setItemIndexMethod(QGraphicsScene::NoIndex);
+    // setItemIndexMethod(QGraphicsScene::NoIndex);
 
-    QList<ChatLine *>::iterator lineIter = _lines.end();
-    QList<ChatLine *>::iterator lineIterBegin = _lines.begin();
+    QList<ChatLine*>::iterator lineIter = _lines.end();
+    QList<ChatLine*>::iterator lineIterBegin = _lines.begin();
     qreal linePos = _sceneRect.y() + _sceneRect.height();
     qreal senderWidth = secondColumnHandle()->sceneLeft() - firstColumnHandle()->sceneRight();
     qreal contentsWidth = _sceneRect.width() - secondColumnHandle()->sceneRight();
@@ -690,17 +664,16 @@ void ChatScene::secondHandlePositionChanged(qreal xpos)
         --lineIter;
         (*lineIter)->setSecondColumn(senderWidth, contentsWidth, contentsPos, linePos);
     }
-    //setItemIndexMethod(QGraphicsScene::BspTreeIndex);
+    // setItemIndexMethod(QGraphicsScene::BspTreeIndex);
 
     updateSceneRect();
     setHandleXLimits();
     emit layoutChanged();
 
-//   clock_t endT = clock();
-//   qDebug() << "resized" << _lines.count() << "in" << (float)(endT - startT) / CLOCKS_PER_SEC << "sec";
+    //   clock_t endT = clock();
+    //   qDebug() << "resized" << _lines.count() << "in" << (float)(endT - startT) / CLOCKS_PER_SEC << "sec";
 }
 
-
 void ChatScene::setHandleXLimits()
 {
     _firstColHandle->setXLimits(0, _secondColHandle->sceneLeft());
@@ -708,15 +681,14 @@ void ChatScene::setHandleXLimits()
     update();
 }
 
-
-void ChatScene::setSelectingItem(ChatItem *item)
+void ChatScene::setSelectingItem(ChatItem* item)
 {
-    if (_selectingItem) _selectingItem->clearSelection();
+    if (_selectingItem)
+        _selectingItem->clearSelection();
     _selectingItem = item;
 }
 
-
-void ChatScene::startGlobalSelection(ChatItem *item, const QPointF &itemPos)
+void ChatScene::startGlobalSelection(ChatItem* item, const QPointF& itemPos)
 {
     _selectionStart = _selectionEnd = _firstSelectionRow = item->row();
     _selectionStartCol = _selectionMinCol = item->column();
@@ -725,13 +697,13 @@ void ChatScene::startGlobalSelection(ChatItem *item, const QPointF &itemPos)
     updateSelection(item->mapToScene(itemPos));
 }
 
-
-void ChatScene::updateSelection(const QPointF &pos)
+void ChatScene::updateSelection(const QPointF& pos)
 {
     int curRow = rowByScenePos(pos);
-    if (curRow < 0) return;
-    int curColumn = (int)columnByScenePos(pos);
-    ChatLineModel::ColumnType minColumn = (ChatLineModel::ColumnType)qMin(curColumn, _selectionStartCol);
+    if (curRow < 0)
+        return;
+    auto curColumn = (int)columnByScenePos(pos);
+    auto minColumn = (ChatLineModel::ColumnType)qMin(curColumn, _selectionStartCol);
     if (minColumn != _selectionMinCol) {
         _selectionMinCol = minColumn;
         for (int l = qMin(_selectionStart, _selectionEnd); l <= qMax(_selectionStart, _selectionEnd); l++) {
@@ -749,11 +721,11 @@ void ChatScene::updateSelection(const QPointF &pos)
             _lines[l]->setSelected(false);
     }
     if (newend > _selectionEnd) {
-        for (int l = _selectionEnd+1; l <= newend; l++)
+        for (int l = _selectionEnd + 1; l <= newend; l++)
             _lines[l]->setSelected(true, minColumn);
     }
     if (newend < _selectionEnd) {
-        for (int l = newend+1; l <= _selectionEnd; l++)
+        for (int l = newend + 1; l <= _selectionEnd; l++)
             _lines[l]->setSelected(false);
     }
 
@@ -772,10 +744,9 @@ void ChatScene::updateSelection(const QPointF &pos)
     }
 }
 
-
-bool ChatScene::isPosOverSelection(const QPointF &pos) const
+bool ChatScene::isPosOverSelection(const QPointF& pos) const
 {
-    ChatItem *chatItem = chatItemAt(pos);
+    ChatItemchatItem = chatItemAt(pos);
     if (!chatItem)
         return false;
     if (hasGlobalSelection()) {
@@ -789,7 +760,6 @@ bool ChatScene::isPosOverSelection(const QPointF &pos) const
     return false;
 }
 
-
 bool ChatScene::isScrollingAllowed() const
 {
     if (_isSelecting)
@@ -800,10 +770,9 @@ bool ChatScene::isScrollingAllowed() const
     return true;
 }
 
-
 /******** MOUSE HANDLING **************************************************************************/
 
-void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
+void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEventevent)
 {
     QPointF pos = event->scenePos();
     QMenu menu;
@@ -813,7 +782,7 @@ void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
     menu.addSeparator();
 
     // item-specific options (select link etc)
-    ChatItem *item = chatItemAt(pos);
+    ChatItemitem = chatItemAt(pos);
     if (item)
         item->addActionsToMenu(&menu, item->mapFromScene(pos));
     else
@@ -822,9 +791,13 @@ void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
 
     // If we have text selected, insert the Copy Selection as first item
     if (isPosOverSelection(pos)) {
-        QAction *sep = menu.insertSeparator(menu.actions().first());
-        QAction *act = new Action(QIcon::fromTheme("edit-copy"), tr("Copy Selection"), &menu, this,
-            SLOT(selectionToClipboard()), QKeySequence::Copy);
+        QAction* sep = menu.insertSeparator(menu.actions().first());
+        QAction* act = new Action(icon::get("edit-copy"),
+                                  tr("Copy Selection"),
+                                  &menu,
+                                  this,
+                                  [this]() { selectionToClipboard(); },
+                                  QKeySequence::Copy);
         menu.insertAction(sep, act);
 
         QString searchSelectionText = selection();
@@ -832,7 +805,7 @@ void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
             searchSelectionText = searchSelectionText.left(_webSearchSelectionTextMaxVisible).append(QString::fromUtf8("…"));
         searchSelectionText = tr("Search '%1'").arg(searchSelectionText);
 
-        QAction *webSearchAction = new Action(QIcon::fromTheme("edit-find"), searchSelectionText, &menu, this, SLOT(webSearchOnSelection()));
+        QAction* webSearchAction = new Action(icon::get("edit-find"), searchSelectionText, &menu, this, &ChatScene::webSearchOnSelection);
         menu.insertAction(sep, webSearchAction);
     }
 
@@ -840,15 +813,14 @@ void ChatScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
         menu.addAction(QtUi::actionCollection("General")->action("ToggleMenuBar"));
 
     // show column reset action if columns have been resized in this session or there is at least one very narrow column
-    if ((_firstColHandlePos != _defaultFirstColHandlePos) || (_secondColHandlePos != _defaultSecondColHandlePos) ||
-        (_firstColHandlePos <= 10) || (_secondColHandlePos - _firstColHandlePos <= 10))
-        menu.addAction(new Action(tr("Reset Column Widths"), &menu, this, SLOT(resetColumnWidths()), 0));
+    if ((_firstColHandlePos != _defaultFirstColHandlePos) || (_secondColHandlePos != _defaultSecondColHandlePos)
+        || (_firstColHandlePos <= 10) || (_secondColHandlePos - _firstColHandlePos <= 10))
+        menu.addAction(new Action(tr("Reset Column Widths"), &menu, this, &ChatScene::resetColumnWidths));
 
     menu.exec(event->screenPos());
 }
 
-
-void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
 {
     if (event->buttons() == Qt::LeftButton) {
         if (!_clickHandled && (event->scenePos() - _clickPos).toPoint().manhattanLength() >= QApplication::startDragDistance()) {
@@ -874,8 +846,7 @@ void ChatScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
         QGraphicsScene::mouseMoveEvent(event);
 }
 
-
-void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
+void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
 {
     if (event->buttons() == Qt::LeftButton) {
         _leftButtonPressed = true;
@@ -887,13 +858,17 @@ void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
         if (_clickMode != NoClick && _clickTimer.isActive()) {
             switch (_clickMode) {
             case NoClick:
-                _clickMode = SingleClick; break;
+                _clickMode = SingleClick;
+                break;
             case SingleClick:
-                _clickMode = DoubleClick; break;
+                _clickMode = DoubleClick;
+                break;
             case DoubleClick:
-                _clickMode = TripleClick; break;
+                _clickMode = TripleClick;
+                break;
             case TripleClick:
-                _clickMode = DoubleClick; break;
+                _clickMode = DoubleClick;
+                break;
             case DragStartClick:
                 break;
             }
@@ -911,15 +886,13 @@ void ChatScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
         QGraphicsScene::mousePressEvent(event);
 }
 
-
-void ChatScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+void ChatScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event)
 {
     // we check for doubleclick ourselves, so just call press handler
     mousePressEvent(event);
 }
 
-
-void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
 {
     if (event->button() == Qt::LeftButton && _leftButtonPressed) {
         _leftButtonPressed = false;
@@ -943,21 +916,19 @@ void ChatScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
     QGraphicsScene::mouseReleaseEvent(event);
 }
 
-
 void ChatScene::clickTimeout()
 {
     if (!_leftButtonPressed && _clickMode == SingleClick)
         handleClick(Qt::LeftButton, _clickPos);
 }
 
-
-void ChatScene::handleClick(Qt::MouseButton button, const QPointF &scenePos)
+void ChatScene::handleClick(Qt::MouseButton button, const QPointF& scenePos)
 {
     if (button == Qt::LeftButton) {
         clearSelection();
 
         // Now send click down to items
-        ChatItem *chatItem = chatItemAt(scenePos);
+        ChatItemchatItem = chatItemAt(scenePos);
         if (chatItem) {
             chatItem->handleClick(chatItem->mapFromScene(scenePos), _clickMode);
         }
@@ -965,18 +936,16 @@ void ChatScene::handleClick(Qt::MouseButton button, const QPointF &scenePos)
     }
 }
 
-
-void ChatScene::initiateDrag(QWidget *source)
+void ChatScene::initiateDrag(QWidget* source)
 {
-    QDrag *drag = new QDrag(source);
-    QMimeData *mimeData = new QMimeData;
+    auto* drag = new QDrag(source);
+    auto* mimeData = new QMimeData;
     mimeData->setText(selection());
     drag->setMimeData(mimeData);
 
     drag->exec(Qt::CopyAction);
 }
 
-
 /******** SELECTIONS ******************************************************************************/
 
 void ChatScene::selectionToClipboard(QClipboard::Mode mode)
@@ -987,8 +956,7 @@ void ChatScene::selectionToClipboard(QClipboard::Mode mode)
     stringToClipboard(selection(), mode);
 }
 
-
-void ChatScene::stringToClipboard(const QString &str_, QClipboard::Mode mode)
+void ChatScene::stringToClipboard(const QString& str_, QClipboard::Mode mode)
 {
     QString str = str_;
     // remove trailing linefeeds
@@ -1008,11 +976,10 @@ void ChatScene::stringToClipboard(const QString &str_, QClipboard::Mode mode)
     };
 }
 
-
 //!\brief Convert current selection to human-readable string.
 QString ChatScene::selection() const
 {
-    //TODO Make selection format configurable!
+    // TODO Make selection format configurable!
     if (hasGlobalSelection()) {
         int start = qMin(_selectionStart, _selectionEnd);
         int end = qMax(_selectionStart, _selectionEnd);
@@ -1021,11 +988,36 @@ QString ChatScene::selection() const
             return QString();
         }
         QString result;
+
         for (int l = start; l <= end; l++) {
-            if (_selectionMinCol == ChatLineModel::TimestampColumn)
-                result += _lines[l]->item(ChatLineModel::TimestampColumn)->data(MessageModel::DisplayRole).toString() + " ";
-            if (_selectionMinCol <= ChatLineModel::SenderColumn)
-                result += _lines[l]->item(ChatLineModel::SenderColumn)->data(MessageModel::DisplayRole).toString() + " ";
+            if (_selectionMinCol == ChatLineModel::TimestampColumn) {
+                ChatItem* item = _lines[l]->item(ChatLineModel::TimestampColumn);
+                if (!_showSenderBrackets && !_timestampHasBrackets) {
+                    // Only re-add brackets if the current timestamp format does not include them
+                    // -and- sender brackets are disabled.  Don't filter on Message::Plain as
+                    // timestamp brackets affect all types of messages.
+                    // Remove any spaces before and after, otherwise it may look weird.
+                    result += QString("[%1] ").arg(item->data(MessageModel::DisplayRole).toString().trimmed());
+                }
+                else {
+                    result += item->data(MessageModel::DisplayRole).toString() + " ";
+                }
+            }
+            if (_selectionMinCol <= ChatLineModel::SenderColumn) {
+                ChatItem* item = _lines[l]->item(ChatLineModel::SenderColumn);
+                if (!_showSenderBrackets && (_alwaysBracketSender || item->chatLine()->msgType() == Message::Plain)) {
+                    // Copying to plain-text.  Re-add the sender brackets if they're normally hidden
+                    // for...
+                    // * Plain messages
+                    // * All messages in the Chat Monitor
+                    //
+                    // The Chat Monitor sets alwaysBracketSender() to true.
+                    result += QString("<%1> ").arg(item->data(MessageModel::DisplayRole).toString());
+                }
+                else {
+                    result += item->data(MessageModel::DisplayRole).toString() + " ";
+                }
+            }
             result += _lines[l]->item(ChatLineModel::ContentsColumn)->data(MessageModel::DisplayRole).toString() + "\n";
         }
         return result;
@@ -1035,25 +1027,21 @@ QString ChatScene::selection() const
     return QString();
 }
 
-
 bool ChatScene::hasSelection() const
 {
     return hasGlobalSelection() || (selectingItem() && selectingItem()->hasSelection());
 }
 
-
 bool ChatScene::hasGlobalSelection() const
 {
     return _selectionStart >= 0;
 }
 
-
 bool ChatScene::isGloballySelecting() const
 {
     return _isSelecting;
 }
 
-
 void ChatScene::clearGlobalSelection()
 {
     if (hasGlobalSelection()) {
@@ -1064,7 +1052,6 @@ void ChatScene::clearGlobalSelection()
     }
 }
 
-
 void ChatScene::clearSelection()
 {
     clearGlobalSelection();
@@ -1072,7 +1059,6 @@ void ChatScene::clearSelection()
         selectingItem()->clearSelection();
 }
 
-
 /******** *************************************************************************************/
 
 void ChatScene::webSearchOnSelection()
@@ -1087,18 +1073,16 @@ void ChatScene::webSearchOnSelection()
     QDesktopServices::openUrl(url);
 }
 
-
 /******** *************************************************************************************/
 
 void ChatScene::requestBacklog()
 {
-    MessageFilter *filter = qobject_cast<MessageFilter *>(model());
+    auto* filter = qobject_cast<MessageFilter*>(model());
     if (filter)
         return filter->requestBacklog();
     return;
 }
 
-
 ChatLineModel::ColumnType ChatScene::columnByScenePos(qreal x) const
 {
     if (x < _firstColHandle->x())
@@ -1109,21 +1093,19 @@ ChatLineModel::ColumnType ChatScene::columnByScenePos(qreal x) const
     return ChatLineModel::ContentsColumn;
 }
 
-
 int ChatScene::rowByScenePos(qreal y) const
 {
-    QList<QGraphicsItem *> itemList = items(QPointF(0, y));
+    QList<QGraphicsItem*> itemList = items(QPointF(0, y));
 
     // ChatLine should be at the bottom of the list
-    for (int i = itemList.count()-1; i >= 0; i--) {
-        ChatLine *line = qgraphicsitem_cast<ChatLine *>(itemList.at(i));
+    for (int i = itemList.count() - 1; i >= 0; i--) {
+        auto* line = qgraphicsitem_cast<ChatLine*>(itemList.at(i));
         if (line)
             return line->row();
     }
     return -1;
 }
 
-
 void ChatScene::updateSceneRect(qreal width)
 {
     if (_lines.isEmpty()) {
@@ -1151,8 +1133,8 @@ void ChatScene::updateSceneRect(qreal width)
 
     // the following call should be safe. If it crashes something went wrong during insert/remove
     if (_firstLineRow < _lines.count()) {
-        ChatLine *firstLine = _lines.at(_firstLineRow);
-        ChatLine *lastLine = _lines.last();
+        ChatLinefirstLine = _lines.at(_firstLineRow);
+        ChatLinelastLine = _lines.last();
         updateSceneRect(QRectF(0, firstLine->pos().y(), width, lastLine->pos().y() + lastLine->height() - firstLine->pos().y()));
     }
     else {
@@ -1161,20 +1143,18 @@ void ChatScene::updateSceneRect(qreal width)
     }
 }
 
-
-void ChatScene::updateSceneRect(const QRectF &rect)
+void ChatScene::updateSceneRect(const QRectF& rect)
 {
     _sceneRect = rect;
     setSceneRect(rect);
     update();
 }
 
-
 // ========================================
-//  Webkit Only stuff
+//  Webkit/WebEngine Only stuff
 // ========================================
-#ifdef HAVE_WEBKIT
-void ChatScene::loadWebPreview(ChatItem *parentItem, const QUrl &url, const QRectF &urlRect)
+#if defined HAVE_WEBKIT || defined HAVE_WEBENGINE
+void ChatScene::loadWebPreview(ChatItem* parentItem, const QUrl& url, const QRectF& urlRect)
 {
     if (!_showWebPreview)
         return;
@@ -1192,7 +1172,7 @@ void ChatScene::loadWebPreview(ChatItem *parentItem, const QUrl &url, const QRec
             if (webPreview.previewItem->scene())
                 removeItem(webPreview.previewItem);
             delete webPreview.previewItem;
-            webPreview.previewItem = 0;
+            webPreview.previewItem = nullptr;
         }
         webPreview.previewState = WebPreview::NoPreview;
     }
@@ -1220,7 +1200,6 @@ void ChatScene::loadWebPreview(ChatItem *parentItem, const QUrl &url, const QRec
     // qDebug() << "  new State:" << webPreview.previewState << webPreview.timer.isActive();
 }
 
-
 void ChatScene::webPreviewNextStep()
 {
     // qDebug() << Q_FUNC_INFO << webPreview.previewState;
@@ -1253,15 +1232,18 @@ void ChatScene::webPreviewNextStep()
     case WebPreview::ShowPreview:
         qWarning() << "ChatScene::webPreviewNextStep() called while in ShowPreview Step!";
         qWarning() << "removing preview";
-        if (webPreview.previewItem && webPreview.previewItem->scene())
+        if (webPreview.previewItem && webPreview.previewItem->scene()) {
             removeItem(webPreview.previewItem);
-    // Fall through to deletion!
+        }
+
+        // Intentional fallthrough
+
     case WebPreview::HidePreview:
         if (webPreview.previewItem) {
             delete webPreview.previewItem;
-            webPreview.previewItem = 0;
+            webPreview.previewItem = nullptr;
         }
-        webPreview.parentItem = 0;
+        webPreview.parentItem = nullptr;
         webPreview.url = QUrl();
         webPreview.urlRect = QRectF();
         webPreview.previewState = WebPreview::NoPreview;
@@ -1269,20 +1251,21 @@ void ChatScene::webPreviewNextStep()
     // qDebug() << "  new State:" << webPreview.previewState << webPreview.timer.isActive();
 }
 
-
-void ChatScene::clearWebPreview(ChatItem *parentItem)
+void ChatScene::clearWebPreview(ChatItem* parentItem)
 {
     // qDebug() << Q_FUNC_INFO << webPreview.previewState;
     switch (webPreview.previewState) {
     case WebPreview::NewPreview:
-        webPreview.previewState = WebPreview::NoPreview; // we haven't loaded anything yet
+        webPreview.previewState = WebPreview::NoPreview;  // we haven't loaded anything yet
         break;
     case WebPreview::ShowPreview:
-        if (parentItem == 0 || webPreview.parentItem == parentItem) {
+        if (parentItem == nullptr || webPreview.parentItem == parentItem) {
             if (webPreview.previewItem && webPreview.previewItem->scene())
                 removeItem(webPreview.previewItem);
         }
-    // fall through into to set hidden state
+
+        // Intentional fallthrough
+
     case WebPreview::DelayPreview:
         // we're just loading, so haven't shown the preview yet.
         webPreview.previewState = WebPreview::HidePreview;
@@ -1295,15 +1278,67 @@ void ChatScene::clearWebPreview(ChatItem *parentItem)
     // qDebug() << "  new State:" << webPreview.previewState << webPreview.timer.isActive();
 }
 
-
 #endif
 
 // ========================================
 //  end of webkit only
 // ========================================
 
+// Local configuration caching
 void ChatScene::showWebPreviewChanged()
 {
     ChatViewSettings settings;
     _showWebPreview = settings.showWebPreview();
 }
+
+void ChatScene::showSenderBracketsChanged()
+{
+    ChatViewSettings settings;
+    _showSenderBrackets = settings.showSenderBrackets();
+}
+
+void ChatScene::useCustomTimestampFormatChanged()
+{
+    ChatViewSettings settings;
+    _useCustomTimestampFormat = settings.useCustomTimestampFormat();
+    updateTimestampHasBrackets();
+}
+
+void ChatScene::timestampFormatStringChanged()
+{
+    ChatViewSettings settings;
+    _timestampFormatString = settings.timestampFormatString();
+    updateTimestampHasBrackets();
+}
+
+void ChatScene::updateTimestampHasBrackets()
+{
+    // Calculate these parameters only as needed, rather than on-demand
+
+    if (!_useCustomTimestampFormat) {
+        // The default timestamp format string does not have brackets, no need to check.
+        // If UiStyle::updateSystemTimestampFormat() has brackets added, change this, too.
+        _timestampHasBrackets = false;
+    }
+    else {
+        // Does the timestamp format contain brackets?  For example:
+        // Classic: "[hh:mm:ss]"
+        // Modern:  " hh:mm:ss"
+        //
+        // Match groups of any opening or closing brackets - (), {}, [], <>, (>, {], etc:
+        //   ^\s*[({[<].+[)}\]>]\s*$
+        //   [...]    is a character group containing ...
+        //   ^        matches start of string
+        //   \s*      matches any amount of whitespace
+        //   [({[<]   matches (, {, [, or <
+        //   .+       matches one or more characters
+        //   [)}\]>]  matches ), }, ], or >, escaping the ]
+        //   $        matches end of string
+        // Alternatively, if opening and closing brackets must be in pairs, use this:
+        //   (^\s*\(.+\)\s*$)|(^\s*\{.+\}\s*$)|(^\s*\[.+\]\s*$)|(^\s*<.+>\s*$)
+        // Note that '\' must be escaped as '\\'
+        // Helpful interactive website for debugging and explaining:  https://regex101.com/
+        const QRegExp regExpMatchBrackets(R"(^\s*[({[<].+[)}\]>]\s*$)");
+        _timestampHasBrackets = regExpMatchBrackets.exactMatch(_timestampFormatString);
+    }
+}