1 /*
2 Drawpile - a collaborative drawing program.
3
4 Copyright (C) 2018-2019 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 #include "avatarlistmodel.h"
20 #include "../libshared/util/paths.h"
21
22 #include <QDir>
23 #include <QBuffer>
24 #include <QCryptographicHash>
25
AvatarListModel(QObject * parent)26 AvatarListModel::AvatarListModel(QObject *parent)
27 : QAbstractListModel(parent)
28 {
29 }
30
rowCount(const QModelIndex & parent) const31 int AvatarListModel::rowCount(const QModelIndex &parent) const
32 {
33 if(parent.isValid())
34 return 0;
35 return m_avatars.size();
36 }
37
data(const QModelIndex & index,int role) const38 QVariant AvatarListModel::data(const QModelIndex &index, int role) const
39 {
40 if(!index.isValid() || index.row() < 0 || index.row() >= m_avatars.size())
41 return QVariant();
42
43 const Avatar &a = m_avatars.at(index.row());
44
45 switch(role) {
46 case Qt::DisplayRole: return a.icon.isNull() ? a.filename : QString();
47 case Qt::DecorationRole: return a.icon;
48 case FilenameRole: return a.filename;
49 }
50
51 return QVariant();
52 }
53
flags(const QModelIndex & index) const54 Qt::ItemFlags AvatarListModel::flags(const QModelIndex &index) const
55 {
56 Qt::ItemFlags f = Qt::NoItemFlags;
57 if(index.isValid() && index.row() >= 0 && index.row() < m_avatars.size()) {
58 f = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
59 }
60 return f;
61 }
62
removeRows(int row,int count,const QModelIndex & parent)63 bool AvatarListModel::removeRows(int row, int count, const QModelIndex &parent)
64 {
65 if(row<0 || row + count > m_avatars.size() || parent.isValid())
66 return false;
67
68 beginRemoveRows(QModelIndex(), row, row+count-1);
69 while(count--) {
70 if(!m_avatars[row].filename.isEmpty())
71 m_deletions << m_avatars[row].filename;
72 m_avatars.remove(row);
73 }
74 endRemoveRows();
75 return true;
76 }
77
getAvatar(const QString & filename) const78 QModelIndex AvatarListModel::getAvatar(const QString &filename) const
79 {
80 for(int i=0;i<m_avatars.size();++i) {
81 if(m_avatars.at(i).filename == filename)
82 return index(i);
83 }
84
85 return QModelIndex();
86 }
87
addAvatar(const QPixmap & icon)88 void AvatarListModel::addAvatar(const QPixmap &icon)
89 {
90 beginInsertRows(QModelIndex(), m_avatars.size(), m_avatars.size());
91 m_avatars << Avatar {
92 icon,
93 QString()
94 };
95 endInsertRows();
96 }
97
loadAvatars(bool includeBlank)98 void AvatarListModel::loadAvatars(bool includeBlank)
99 {
100 QVector<Avatar> avatars;
101
102 if(includeBlank) {
103 avatars << Avatar {
104 QPixmap(),
105 tr("No avatar")
106 };
107 }
108
109 QDir dir = utils::paths::writablePath("avatars");
110 const QStringList files = dir.entryList(QStringList() << "*.png", QDir::Files|QDir::Readable);
111
112 for(const QString &filename : files) {
113 avatars << Avatar {
114 QPixmap(dir.filePath(filename), "PNG"),
115 filename
116 };
117 }
118
119 beginResetModel();
120 m_avatars = avatars;
121 m_deletions.clear();
122 endResetModel();
123 }
124
commit()125 bool AvatarListModel::commit()
126 {
127 QDir dir = utils::paths::writablePath("avatars", ".");
128
129 // Commit deletions
130 for(const QString &filename : m_deletions) {
131 dir.remove(filename);
132 }
133 m_deletions.clear();
134
135 // Save newly added avatars
136 for(Avatar &a : m_avatars) {
137 if(!a.icon.isNull() && a.filename.isEmpty()) {
138 Q_ASSERT(!a.icon.isNull());
139
140 QBuffer buf;
141 buf.open(QBuffer::ReadWrite);
142
143 a.icon.save(&buf, "PNG");
144 buf.seek(0);
145
146 QCryptographicHash hash(QCryptographicHash::Md5);
147 hash.addData(&buf);
148
149 const QString filename = QString::fromUtf8(hash.result().toHex() + ".png");
150 QFile f { dir.filePath(filename) };
151 if(!f.open(QFile::WriteOnly)) {
152 qWarning("Couldn't save %s", qPrintable(filename));
153 return false;
154 }
155 f.write(buf.data());
156 f.close();
157
158 a.filename = filename;
159 }
160 }
161 return true;
162 }
163