We now handle the marker line position independently from lastSeenMsg in the backend. This will
allow for setting the marker line manually in the future, for people who don't want the automatic
mark-as-read thingy. Currently, it's still set on buffer change, and the behavior should be the same
as before.
Note that this needs an updated core to work correctly, and will be disabled (= forced auto) else.
Q_ASSERT(!_bufferSyncer);
_bufferSyncer = new BufferSyncer(this);
connect(bufferSyncer(), SIGNAL(lastSeenMsgSet(BufferId, MsgId)), _networkModel, SLOT(setLastSeenMsgId(BufferId, MsgId)));
+ connect(bufferSyncer(), SIGNAL(markerLineSet(BufferId,MsgId)), _networkModel, SLOT(setMarkerLineMsgId(BufferId,MsgId)));
connect(bufferSyncer(), SIGNAL(bufferRemoved(BufferId)), this, SLOT(bufferRemoved(BufferId)));
connect(bufferSyncer(), SIGNAL(bufferRenamed(BufferId, QString)), this, SLOT(bufferRenamed(BufferId, QString)));
connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), this, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
}
void Client::setBufferLastSeenMsg(BufferId id, const MsgId &msgId) {
- if(!bufferSyncer())
- return;
- bufferSyncer()->requestSetLastSeenMsg(id, msgId);
+ if(bufferSyncer())
+ bufferSyncer()->requestSetLastSeenMsg(id, msgId);
+}
+
+void Client::setBufferMarkerLine(BufferId id, const MsgId &msgId) {
+ if(bufferSyncer())
+ bufferSyncer()->requestSetMarkerLine(id, msgId);
}
void Client::removeBuffer(BufferId id) {
static void userInput(const BufferInfo &bufferInfo, const QString &message);
static void setBufferLastSeenMsg(BufferId id, const MsgId &msgId); // this is synced to core and other clients
+ static void setBufferMarkerLine(BufferId id, const MsgId &msgId); // this is synced to core and other clients
static void removeBuffer(BufferId id);
static void renameBuffer(BufferId bufferId, const QString &newName);
static void mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2);
}
}
+// FIXME shouldn't we check the bufferItemCache here?
BufferItem *NetworkItem::findBufferItem(BufferId bufferId) {
BufferItem *bufferItem = 0;
void BufferItem::clearActivityLevel() {
_activity = BufferInfo::NoActivity;
- _lastSeenMarkerMsgId = _lastSeenMsgId;
_firstUnreadMsgId = MsgId();
+
+ // FIXME remove with core proto v11
+ if(!Client::coreFeatures() & Quassel::SynchronizedMarkerLine) {
+ _markerLineMsgId = _lastSeenMsgId;
+ }
+
emit dataChanged();
}
return (int)activityLevel();
case NetworkModel::BufferFirstUnreadMsgIdRole:
return qVariantFromValue(firstUnreadMsgId());
+ case NetworkModel::MarkerLineMsgIdRole:
+ return qVariantFromValue(markerLineMsgId());
default:
return PropertyMapItem::data(column, role);
}
emit dataChanged(0);
}
-void BufferItem::setLastSeenMsgId(const MsgId &msgId) {
+void BufferItem::setLastSeenMsgId(MsgId msgId) {
_lastSeenMsgId = msgId;
- if(!isCurrentBuffer()) {
- _lastSeenMarkerMsgId = msgId;
+
+ // FIXME remove with core protocol v11
+ if(!Client::coreFeatures() & Quassel::SynchronizedMarkerLine) {
+ if(!isCurrentBuffer())
+ _markerLineMsgId = msgId;
}
+
setActivityLevel(BufferInfo::NoActivity);
}
+void BufferItem::setMarkerLineMsgId(MsgId msgId) {
+ _markerLineMsgId = msgId;
+ emit dataChanged();
+}
+
bool BufferItem::isCurrentBuffer() const {
return _bufferInfo.bufferId() == Client::bufferModel()->currentIndex().data(NetworkModel::BufferIdRole).value<BufferId>();
}
return _bufferItemCache[bufferId]->lastSeenMsgId();
}
-MsgId NetworkModel::lastSeenMarkerMsgId(BufferId bufferId) const {
+MsgId NetworkModel::markerLineMsgId(BufferId bufferId) const {
if(!_bufferItemCache.contains(bufferId))
return MsgId();
- return _bufferItemCache[bufferId]->lastSeenMarkerMsgId();
+ return _bufferItemCache[bufferId]->markerLineMsgId();
}
+// FIXME we always seem to use this (expensive) non-const version
MsgId NetworkModel::lastSeenMsgId(const BufferId &bufferId) {
BufferItem *bufferItem = findBufferItem(bufferId);
if(!bufferItem) {
bufferItem->setLastSeenMsgId(msgId);
}
+void NetworkModel::setMarkerLineMsgId(const BufferId &bufferId, const MsgId &msgId) {
+ BufferItem *bufferItem = findBufferItem(bufferId);
+ if(!bufferItem) {
+ qDebug() << "NetworkModel::setMarkerLineMsgId(): buffer is unknown:" << bufferId;
+ Client::purgeKnownBufferIds();
+ return;
+ }
+ bufferItem->setMarkerLineMsgId(msgId);
+}
+
void NetworkModel::updateBufferActivity(Message &msg) {
int redirectionTarget = 0;
switch(msg.type()) {
virtual inline bool isActive() const { return qobject_cast<NetworkItem *>(parent())->isActive(); }
- inline const MsgId &lastSeenMsgId() const { return _lastSeenMsgId; }
- inline const MsgId &lastSeenMarkerMsgId() const { return _lastSeenMarkerMsgId; }
- void setLastSeenMsgId(const MsgId &msgId);
+ inline MsgId lastSeenMsgId() const { return _lastSeenMsgId; }
+ inline MsgId markerLineMsgId() const { return _markerLineMsgId; }
+ void setLastSeenMsgId(MsgId msgId);
+ void setMarkerLineMsgId(MsgId msgId);
+
inline BufferInfo::ActivityLevel activityLevel() const { return _activity; }
void setActivityLevel(BufferInfo::ActivityLevel level);
void clearActivityLevel();
BufferInfo _bufferInfo;
BufferInfo::ActivityLevel _activity;
MsgId _lastSeenMsgId;
- MsgId _lastSeenMarkerMsgId;
+ MsgId _markerLineMsgId;
MsgId _firstUnreadMsgId;
};
IrcUserRole,
IrcChannelRole,
BufferFirstUnreadMsgIdRole,
+ MarkerLineMsgIdRole,
};
enum ItemType {
BufferInfo::Type bufferType(BufferId bufferId) const;
BufferInfo bufferInfo(BufferId bufferId) const;
MsgId lastSeenMsgId(BufferId bufferId) const;
- MsgId lastSeenMarkerMsgId(BufferId bufferId) const;
+ MsgId markerLineMsgId(BufferId bufferId) const;
NetworkId networkId(BufferId bufferId) const;
QString networkName(BufferId bufferId) const;
void removeBuffer(BufferId bufferId);
MsgId lastSeenMsgId(const BufferId &bufferId);
void setLastSeenMsgId(const BufferId &bufferId, const MsgId &msgId);
+ void setMarkerLineMsgId(const BufferId &bufferId, const MsgId &msgId);
void setBufferActivity(const BufferId &bufferId, BufferInfo::ActivityLevel activity);
void clearBufferActivity(const BufferId &bufferId);
void updateBufferActivity(Message &msg);
{
}
-BufferSyncer::BufferSyncer(const QHash<BufferId, MsgId> &lastSeenMsg, QObject *parent)
+BufferSyncer::BufferSyncer(const QHash<BufferId, MsgId> &lastSeenMsg, const QHash<BufferId, MsgId> &markerLines, QObject *parent)
: SyncableObject(parent),
- _lastSeenMsg(lastSeenMsg)
+ _lastSeenMsg(lastSeenMsg),
+ _markerLines(markerLines)
{
}
MsgId BufferSyncer::lastSeenMsg(BufferId buffer) const {
- if(_lastSeenMsg.contains(buffer))
- return _lastSeenMsg[buffer];
- return MsgId();
+ return _lastSeenMsg.value(buffer, MsgId());
}
bool BufferSyncer::setLastSeenMsg(BufferId buffer, const MsgId &msgId) {
return false;
}
+MsgId BufferSyncer::markerLine(BufferId buffer) const {
+ return _markerLines.value(buffer, MsgId());
+}
+
+bool BufferSyncer::setMarkerLine(BufferId buffer, const MsgId &msgId) {
+ if(!msgId.isValid())
+ return false;
+
+ _markerLines[buffer] = msgId;
+ SYNC(ARG(buffer), ARG(msgId))
+ emit markerLineSet(buffer, msgId);
+ return true;
+}
+
QVariantList BufferSyncer::initLastSeenMsg() const {
QVariantList list;
QHash<BufferId, MsgId>::const_iterator iter = _lastSeenMsg.constBegin();
while(iter != _lastSeenMsg.constEnd()) {
list << QVariant::fromValue<BufferId>(iter.key())
- << QVariant::fromValue<MsgId>(iter.value());
- iter++;
+ << QVariant::fromValue<MsgId>(iter.value());
+ ++iter;
}
return list;
}
_lastSeenMsg.clear();
Q_ASSERT(list.count() % 2 == 0);
for(int i = 0; i < list.count(); i += 2) {
- setLastSeenMsg(list[i].value<BufferId>(), list[i+1].value<MsgId>());
+ setLastSeenMsg(list.at(i).value<BufferId>(), list.at(i+1).value<MsgId>());
+ }
+}
+
+QVariantList BufferSyncer::initMarkerLines() const {
+ QVariantList list;
+ QHash<BufferId, MsgId>::const_iterator iter = _markerLines.constBegin();
+ while(iter != _markerLines.constEnd()) {
+ list << QVariant::fromValue<BufferId>(iter.key())
+ << QVariant::fromValue<MsgId>(iter.value());
+ ++iter;
+ }
+ return list;
+}
+
+void BufferSyncer::initSetMarkerLines(const QVariantList &list) {
+ _markerLines.clear();
+ Q_ASSERT(list.count() % 2 == 0);
+ for(int i = 0; i < list.count(); i += 2) {
+ setMarkerLine(list.at(i).value<BufferId>(), list.at(i+1).value<MsgId>());
}
}
void BufferSyncer::removeBuffer(BufferId buffer) {
if(_lastSeenMsg.contains(buffer))
_lastSeenMsg.remove(buffer);
+ if(_markerLines.contains(buffer))
+ _markerLines.remove(buffer);
SYNC(ARG(buffer))
emit bufferRemoved(buffer);
}
void BufferSyncer::mergeBuffersPermanently(BufferId buffer1, BufferId buffer2) {
if(_lastSeenMsg.contains(buffer2))
_lastSeenMsg.remove(buffer2);
+ if(_markerLines.contains(buffer2))
+ _markerLines.remove(buffer2);
SYNC(ARG(buffer1), ARG(buffer2))
emit buffersPermanentlyMerged(buffer1, buffer2);
}
public:
explicit BufferSyncer(QObject *parent);
- explicit BufferSyncer(const QHash<BufferId, MsgId> &lastSeenMsg, QObject *parent);
+ explicit BufferSyncer(const QHash<BufferId, MsgId> &lastSeenMsg, const QHash<BufferId, MsgId> &markerLines, QObject *parent);
inline virtual const QMetaObject *syncMetaObject() const { return &staticMetaObject; }
MsgId lastSeenMsg(BufferId buffer) const;
+ MsgId markerLine(BufferId buffer) const;
public slots:
QVariantList initLastSeenMsg() const;
void initSetLastSeenMsg(const QVariantList &);
+ QVariantList initMarkerLines() const;
+ void initSetMarkerLines(const QVariantList &);
+
virtual inline void requestSetLastSeenMsg(BufferId buffer, const MsgId &msgId) { REQUEST(ARG(buffer), ARG(msgId)) }
+ virtual inline void requestSetMarkerLine(BufferId buffer, const MsgId &msgId) { REQUEST(ARG(buffer), ARG(msgId)) }
virtual inline void requestRemoveBuffer(BufferId buffer) { REQUEST(ARG(buffer)) }
virtual void removeBuffer(BufferId buffer);
signals:
void lastSeenMsgSet(BufferId buffer, const MsgId &msgId);
+ void markerLineSet(BufferId buffer, const MsgId &msgId);
void bufferRemoved(BufferId buffer);
void bufferRenamed(BufferId buffer, QString newName);
void buffersPermanentlyMerged(BufferId buffer1, BufferId buffer2);
protected slots:
bool setLastSeenMsg(BufferId buffer, const MsgId &msgId);
- QList<BufferId> bufferIds() const { return _lastSeenMsg.keys(); }
+ bool setMarkerLine(BufferId buffer, const MsgId &msgId);
+
+protected:
+ inline QList<BufferId> lastSeenBufferIds() const { return _lastSeenMsg.keys(); }
+ inline QList<BufferId> markerLineBufferIds() const { return _markerLines.keys(); }
+ inline QHash<BufferId, MsgId> markerLines() const { return _markerLines; }
private:
QHash<BufferId, MsgId> _lastSeenMsg;
+ QHash<BufferId, MsgId> _markerLines;
};
#endif
INIT_SYNCABLE_OBJECT(CoreBufferSyncer)
CoreBufferSyncer::CoreBufferSyncer(CoreSession *parent)
- : BufferSyncer(Core::bufferLastSeenMsgIds(parent->user()), parent),
+ : BufferSyncer(Core::bufferLastSeenMsgIds(parent->user()), Core::bufferMarkerLineMsgIds(parent->user()), parent),
_coreSession(parent),
_purgeBuffers(false)
{
void CoreBufferSyncer::requestSetLastSeenMsg(BufferId buffer, const MsgId &msgId) {
if(setLastSeenMsg(buffer, msgId))
- dirtyBuffers << buffer;
+ dirtyLastSeenBuffers << buffer;
+}
+
+void CoreBufferSyncer::requestSetMarkerLine(BufferId buffer, const MsgId &msgId) {
+ if(setMarkerLine(buffer, msgId))
+ dirtyMarkerLineBuffers << buffer;
}
void CoreBufferSyncer::storeDirtyIds() {
UserId userId = _coreSession->user();
MsgId msgId;
- foreach(BufferId bufferId, dirtyBuffers) {
+ foreach(BufferId bufferId, dirtyLastSeenBuffers) {
msgId = lastSeenMsg(bufferId);
if(msgId.isValid())
Core::setBufferLastSeenMsg(userId, bufferId, msgId);
}
- dirtyBuffers.clear();
+
+ foreach(BufferId bufferId, dirtyMarkerLineBuffers) {
+ msgId = markerLine(bufferId);
+ if(msgId.isValid())
+ Core::setBufferMarkerLineMsg(userId, bufferId, msgId);
+ }
+
+ dirtyLastSeenBuffers.clear();
+ dirtyMarkerLineBuffers.clear();
}
void CoreBufferSyncer::removeBuffer(BufferId bufferId) {
actualBuffers << bufferInfo.bufferId();
}
- QList<BufferId> storedIds = bufferIds();
+ QSet<BufferId> storedIds = lastSeenBufferIds().toSet() + markerLineBufferIds().toSet();
foreach(BufferId bufferId, storedIds) {
if(!actualBuffers.contains(bufferId)) {
BufferSyncer::removeBuffer(bufferId);
public slots:
virtual void requestSetLastSeenMsg(BufferId buffer, const MsgId &msgId);
+ virtual void requestSetMarkerLine(BufferId buffer, const MsgId &msgId);
virtual inline void requestRemoveBuffer(BufferId buffer) { removeBuffer(buffer); }
virtual void removeBuffer(BufferId bufferId);
CoreSession *_coreSession;
bool _purgeBuffers;
- QSet<BufferId> dirtyBuffers;
+ QSet<BufferId> dirtyLastSeenBuffers;
+ QSet<BufferId> dirtyMarkerLineBuffers;
void purgeBufferIds();
};
#include "buffermodel.h"
#include "bufferview.h"
#include "chatitem.h"
+#include "chatline.h"
#include "chatlinemodel.h"
#include "contextmenuactionprovider.h"
#include "iconloader.h"
setPos(pos);
}
+const QAbstractItemModel *ChatItem::model() const {
+ return static_cast<ChatLine *>(parentItem())->model();
+}
+
+int ChatItem::row() const {
+ return static_cast<ChatLine *>(parentItem())->row();
+}
+
QVariant ChatItem::data(int role) const {
QModelIndex index = model()->index(row(), column());
if(!index.isValid()) {
ChatItem(const qreal &width, const qreal &height, const QPointF &pos, QGraphicsItem *parent);
public:
- inline const QAbstractItemModel *model() const;
- inline int row() const;
+ const QAbstractItemModel *model() const;
+ int row() const;
virtual ChatLineModel::ColumnType column() const = 0;
inline ChatScene *chatScene() const { return qobject_cast<ChatScene *>(scene()); }
/*************************************************************************************************/
-// Avoid circular include deps
-#include "chatline.h"
-const QAbstractItemModel *ChatItem::model() const { return static_cast<ChatLine *>(parentItem())->model(); }
-int ChatItem::row() const { return static_cast<ChatLine *>(parentItem())->row(); }
-
#endif
// don't show the marker if we wrote that new line
if(!(flags & Message::Self)) {
BufferId bufferId = BufferId(chatScene()->idString().toInt());
- MsgId lastSeenMsgId = Client::networkModel()->lastSeenMarkerMsgId(bufferId);
+ MsgId lastSeenMsgId = Client::networkModel()->markerLineMsgId(bufferId);
if(lastSeenMsgId < myMsgId && lastSeenMsgId >= prevMsgId) {
QLinearGradient gradient(0, 0, 0, contentsItem().fontMetrics()->lineSpacing());
gradient.setColorAt(0, QtUi::style()->brush(UiStyle::MarkerLine).color()); // FIXME: Use full (gradient?) brush instead of just the color
#include <QPainter>
#include "chatitem.h"
+#include "chatline.h"
#include "chatlinemodel.h"
#include "chatscene.h"
#include "messagemodel.h"
void AbstractBufferContainer::setCurrentBuffer(BufferId bufferId) {
BufferId prevBufferId = currentBuffer();
if(prevBufferId.isValid() && _chatViews.contains(prevBufferId)) {
- Client::setBufferLastSeenMsg(prevBufferId, _chatViews[prevBufferId]->lastMsgId());
+ MsgId msgId = _chatViews.value(prevBufferId)->lastMsgId();
+ Client::setBufferLastSeenMsg(prevBufferId, msgId);
+ if(autoSetMarkerLine())
+ Client::setBufferMarkerLine(prevBufferId, msgId);
}
if(!bufferId.isValid()) {
_currentBuffer = bufferId;
showChatView(bufferId);
Client::networkModel()->clearBufferActivity(bufferId);
+ Client::setBufferLastSeenMsg(bufferId, _chatViews[bufferId]->lastMsgId());
Client::backlogManager()->checkForBacklog(bufferId);
setFocus();
-
- if(bufferId.isValid() && _chatViews.contains(bufferId)) {
- Client::setBufferLastSeenMsg(bufferId, _chatViews[bufferId]->lastMsgId());
- }
}
*/
virtual void removeChatView(BufferId) = 0;
+ virtual inline bool autoSetMarkerLine() const { return true; }
+
protected slots:
virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);