1 /*
2    Drawpile - a collaborative drawing program.
3 
4    Copyright (C) 2015-2018 Calle Laakkonen
5 
6    Drawpile is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10 
11    Drawpile is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with Drawpile.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "usercursormodel.h"
21 #include "layerlist.h"
22 
23 #include <QDateTime>
24 #include <QTimerEvent>
25 #include <QDebug>
26 
27 namespace canvas {
28 
29 // Hide cursors after they have not moved for this many milliseconds
30 static const qint64 AUTOHIDE_TIME = 3000;
31 
UserCursorModel(QObject * parent)32 UserCursorModel::UserCursorModel(QObject *parent)
33 	: QAbstractListModel(parent), m_layerlist(nullptr)
34 {
35 	m_timerId = startTimer(1000, Qt::VeryCoarseTimer);
36 }
37 
rowCount(const QModelIndex & parent) const38 int UserCursorModel::rowCount(const QModelIndex &parent) const
39 {
40 	if(parent.isValid())
41 		return 0;
42 	return m_cursors.size();
43 }
44 
indexForId(int id) const45 QModelIndex UserCursorModel::indexForId(int id) const
46 {
47 	for(int i=0;i<m_cursors.size();++i)
48 		if(m_cursors.at(i).id == id)
49 			return index(i);
50 	return QModelIndex();
51 }
52 
data(const QModelIndex & index,int role) const53 QVariant UserCursorModel::data(const QModelIndex &index, int role) const
54 {
55 	if(index.isValid() && index.row() >= 0 && index.row() < m_cursors.size()) {
56 		const UserCursor &uc = m_cursors.at(index.row());
57 	switch(role) {
58 		case Qt::DisplayRole: return uc.name;
59 		case Qt::DecorationRole: return uc.avatar;
60 		case IdRole: return uc.id;
61 		case PositionRole: return uc.pos;
62 		case LayerRole: return uc.layer;
63 		case ColorRole: return uc.color;
64 		case VisibleRole: return uc.visible;
65 		default: break;
66 		}
67 	}
68 	return QVariant();
69 }
70 
roleNames() const71 QHash<int, QByteArray> UserCursorModel::roleNames() const
72 {
73 	QHash<int, QByteArray> roles;
74 	roles[Qt::DisplayRole] = "display";
75 	roles[Qt::DecorationRole] = "decoration";
76 	roles[IdRole] = "id";
77 	roles[PositionRole] = "pos";
78 	roles[LayerRole] = "layer";
79 	roles[ColorRole] = "color";
80 	roles[VisibleRole] = "visible";
81 	return roles;
82 }
83 
84 
setCursorName(int id,const QString & name)85 void UserCursorModel::setCursorName(int id, const QString &name)
86 {
87 	QModelIndex index;
88 	UserCursor *uc = getOrCreate(id, index);
89 
90 	uc->name = name;
91 
92 	emit dataChanged(index, index, QVector<int>() << Qt::DisplayRole);
93 }
94 
setCursorAvatar(int id,const QPixmap & avatar)95 void UserCursorModel::setCursorAvatar(int id, const QPixmap &avatar)
96 {
97 	QModelIndex index;
98 	UserCursor *uc = getOrCreate(id, index);
99 
100 	uc->avatar = avatar;
101 
102 	emit dataChanged(index, index, QVector<int>() << Qt::DecorationRole);
103 }
104 
setCursorColor(int id,const QColor & color)105 void UserCursorModel::setCursorColor(int id, const QColor &color)
106 {
107 	QModelIndex index;
108 	UserCursor *uc = getOrCreate(id, index);
109 
110 	uc->color = color;
111 
112 	emit dataChanged(index, index, QVector<int>() << ColorRole);
113 }
114 
setCursorPosition(int id,int layerId,const QPoint & pos)115 void UserCursorModel::setCursorPosition(int id, int layerId, const QPoint &pos)
116 {
117 	QModelIndex index;
118 	UserCursor *uc = getOrCreate(id, index);
119 
120 	QVector<int> roles;
121 
122 	uc->pos = pos; roles << PositionRole;
123 	uc->lastMoved = QDateTime::currentMSecsSinceEpoch();
124 	if(!uc->visible) {
125 		uc->visible = true;
126 		roles << VisibleRole;
127 	}
128 
129 	if(layerId>0 && layerId != uc->layerId) {
130 		uc->layerId = layerId;
131 		QString layerName = QStringLiteral("???");
132 		if(m_layerlist) {
133 			QVariant ln = m_layerlist->layerIndex(layerId).data(LayerListModel::TitleRole);
134 			if(!ln.isNull())
135 				layerName = ln.toString();
136 		}
137 		uc->layer = layerName;
138 		roles << LayerRole;
139 	}
140 
141 	emit dataChanged(index, index, roles);
142 }
143 
hideCursor(int id)144 void UserCursorModel::hideCursor(int id)
145 {
146 	for(int i=0;i<m_cursors.size();++i) {
147 		if(m_cursors.at(i).id == id) {
148 			// Leave the cursor visible for just a little while longer
149 			// This makes it easier to catch who drew something and gives
150 			// the navigator time to render the marker
151 			m_cursors[i].lastMoved = QDateTime::currentMSecsSinceEpoch() - AUTOHIDE_TIME + 1000;
152 
153 			QModelIndex idx = index(i);
154 			emit dataChanged(idx, idx, QVector<int>() << VisibleRole);
155 			return;
156 		}
157 	}
158 }
159 
removeCursor(int id)160 void UserCursorModel::removeCursor(int id)
161 {
162 	for(int i=0;i<m_cursors.size();++i) {
163 		if(m_cursors.at(i).id == id) {
164 			beginRemoveRows(QModelIndex(), i, i);
165 			m_cursors.remove(i);
166 			endRemoveRows();
167 			return;
168 		}
169 	}
170 }
171 
clear()172 void UserCursorModel::clear()
173 {
174 	beginResetModel();
175 	m_cursors.clear();
176 	endResetModel();
177 }
178 
getOrCreate(int id,QModelIndex & idx)179 UserCursor *UserCursorModel::getOrCreate(int id, QModelIndex &idx)
180 {
181 	for(int i=0;i<m_cursors.size();++i) {
182 		if(m_cursors.at(i).id == id) {
183 			idx = index(i);
184 			return &m_cursors[i];
185 		}
186 	}
187 
188 	beginInsertRows(QModelIndex(), m_cursors.size(), m_cursors.size());
189 	m_cursors.append(UserCursor {
190 		id,
191 		false,
192 		QDateTime::currentMSecsSinceEpoch(),
193 		0,
194 		QPoint(),
195 		QStringLiteral("#%1").arg(id),
196 		QString(),
197 		QColor(Qt::black),
198 		QPixmap()
199 	});
200 	endInsertRows();
201 
202 	idx = index(m_cursors.size()-1);
203 	return &m_cursors[m_cursors.size()-1];
204 }
205 
timerEvent(QTimerEvent * e)206 void UserCursorModel::timerEvent(QTimerEvent *e)
207 {
208 	if(e->timerId() != m_timerId) {
209 		QAbstractListModel::timerEvent(e);
210 		return;
211 	}
212 
213 	const qint64 now = QDateTime::currentMSecsSinceEpoch();
214 	const qint64 hideTreshold = now - AUTOHIDE_TIME;
215 
216 	for(int i=0;i<m_cursors.size();++i) {
217 		UserCursor &uc = m_cursors[i];
218 
219 		if(uc.visible && uc.lastMoved < hideTreshold) {
220 			uc.visible = false;
221 			QModelIndex idx = index(i);
222 			emit dataChanged(idx, idx, QVector<int>() << VisibleRole);
223 		}
224 	}
225 }
226 
227 }
228