1 | #include <qpixmap.h>
|
---|
2 | #include <qbuffer.h>
|
---|
3 | #include <qcstring.h>
|
---|
4 | #include <qdatetime.h>
|
---|
5 | #include <qsize.h>
|
---|
6 | #include <qfile.h>
|
---|
7 |
|
---|
8 | #include "xmpp.h"
|
---|
9 | #include "avatars.h"
|
---|
10 | #include "common.h"
|
---|
11 | #include "iconset.h"
|
---|
12 | #include "psiaccount.h"
|
---|
13 | #include "profiles.h"
|
---|
14 | #include "vcardfactory.h"
|
---|
15 |
|
---|
16 | #define MAX_AVATAR_SIZE 64
|
---|
17 |
|
---|
18 | //------------------------------------------------------------------------------
|
---|
19 |
|
---|
20 | //------------------------------------------------------------------------------
|
---|
21 | // Avatar: Base class for avatars.
|
---|
22 | //------------------------------------------------------------------------------
|
---|
23 |
|
---|
24 | Avatar::Avatar()
|
---|
25 | {
|
---|
26 | }
|
---|
27 |
|
---|
28 |
|
---|
29 | Avatar::~Avatar()
|
---|
30 | {
|
---|
31 | }
|
---|
32 |
|
---|
33 | int Avatar::maxSize()
|
---|
34 | {
|
---|
35 | return (option.avatarsSize > MAX_AVATAR_SIZE ? MAX_AVATAR_SIZE : option.avatarsSize);
|
---|
36 | }
|
---|
37 |
|
---|
38 | void Avatar::setImage(const QImage& i)
|
---|
39 | {
|
---|
40 |
|
---|
41 | if (i.width() > maxSize() || i.height() > option.avatarsSize)
|
---|
42 | pixmap_.convertFromImage(i.smoothScale(maxSize(),maxSize(),QImage::ScaleMin));
|
---|
43 | else
|
---|
44 | pixmap_.convertFromImage(i);
|
---|
45 | }
|
---|
46 |
|
---|
47 | void Avatar::setImage(const QByteArray& ba)
|
---|
48 | {
|
---|
49 | setImage(QImage(ba));
|
---|
50 | }
|
---|
51 |
|
---|
52 | void Avatar::setImage(const QPixmap& p)
|
---|
53 | {
|
---|
54 | if (p.width() > maxSize() || p.height() > maxSize())
|
---|
55 | setImage(p.convertToImage());
|
---|
56 | else
|
---|
57 | pixmap_ = p;
|
---|
58 | }
|
---|
59 |
|
---|
60 | void Avatar::resetImage()
|
---|
61 | {
|
---|
62 | pixmap_ = QPixmap();
|
---|
63 | }
|
---|
64 |
|
---|
65 |
|
---|
66 | //------------------------------------------------------------------------------
|
---|
67 |
|
---|
68 | //------------------------------------------------------------------------------
|
---|
69 | // VCardAvatar: Avatars coming from VCards of contacts.
|
---|
70 | //------------------------------------------------------------------------------
|
---|
71 |
|
---|
72 | class VCardAvatar : public QObject, public Avatar
|
---|
73 | {
|
---|
74 | Q_OBJECT
|
---|
75 |
|
---|
76 | public:
|
---|
77 | VCardAvatar(AvatarFactory* factory, const Jid& jid);
|
---|
78 | QPixmap getPixmap();
|
---|
79 |
|
---|
80 | signals:
|
---|
81 | void avatarChanged(const Jid&);
|
---|
82 |
|
---|
83 | private:
|
---|
84 | Jid jid_;
|
---|
85 | AvatarFactory* factory_;
|
---|
86 | const VCard* lastVCard_; // Never dereference this, might point to dead object
|
---|
87 | };
|
---|
88 |
|
---|
89 |
|
---|
90 | VCardAvatar::VCardAvatar(AvatarFactory* factory, const Jid& jid) :
|
---|
91 | jid_(jid), factory_(factory)
|
---|
92 | {
|
---|
93 | const VCard* vcard = VCardFactory::vcard(jid_);
|
---|
94 | if (vcard && vcard->photo()) {
|
---|
95 | setImage(vcard->photo());
|
---|
96 | }
|
---|
97 | lastVCard_ = vcard;
|
---|
98 | }
|
---|
99 |
|
---|
100 |
|
---|
101 | QPixmap VCardAvatar::getPixmap()
|
---|
102 | {
|
---|
103 | // Try to find an avatar if the current one is empty
|
---|
104 | // FIXME: The pixmap shouldn't be updated by active polling, but by
|
---|
105 | // some signal from the VCard factory when the vcard is updated.
|
---|
106 | const VCard* vcard = VCardFactory::vcard(jid_);
|
---|
107 | if (lastVCard_ != vcard) {
|
---|
108 | if (vcard && vcard->photo()) {
|
---|
109 | setImage(vcard->photo());
|
---|
110 | }
|
---|
111 | else
|
---|
112 | resetImage();
|
---|
113 | lastVCard_ = vcard;
|
---|
114 | emit avatarChanged(jid_);
|
---|
115 | }
|
---|
116 |
|
---|
117 | return pixmap();
|
---|
118 | }
|
---|
119 |
|
---|
120 |
|
---|
121 | //------------------------------------------------------------------------------
|
---|
122 |
|
---|
123 | //------------------------------------------------------------------------------
|
---|
124 | // FileAvatar: Avatars coming from local files.
|
---|
125 | //------------------------------------------------------------------------------
|
---|
126 |
|
---|
127 | class FileAvatar : public Avatar
|
---|
128 | {
|
---|
129 | public:
|
---|
130 | FileAvatar(const Jid& jid);
|
---|
131 | void import(const QString& file);
|
---|
132 | void removeFromDisk();
|
---|
133 | bool exists();
|
---|
134 | QPixmap getPixmap();
|
---|
135 | const Jid& getJid() const
|
---|
136 | { return jid_; }
|
---|
137 |
|
---|
138 | protected:
|
---|
139 | bool isDirty() const;
|
---|
140 | QString getFileName() const;
|
---|
141 | void refresh();
|
---|
142 | QDateTime lastModified() const
|
---|
143 | { return lastModified_; }
|
---|
144 |
|
---|
145 | private:
|
---|
146 | QDateTime lastModified_;
|
---|
147 | Jid jid_;
|
---|
148 | };
|
---|
149 |
|
---|
150 |
|
---|
151 | FileAvatar::FileAvatar(const Jid& jid) : jid_(jid)
|
---|
152 | {
|
---|
153 | }
|
---|
154 |
|
---|
155 | void FileAvatar::import(const QString& file)
|
---|
156 | {
|
---|
157 | if (QFileInfo(file).exists()) {
|
---|
158 | // Check if the manual dir exists
|
---|
159 | if (!QDir(AvatarFactory::getAvatarsDir()).exists())
|
---|
160 | QDir("").mkdir(AvatarFactory::getAvatarsDir());
|
---|
161 | if (!QDir(AvatarFactory::getManualDir()).exists())
|
---|
162 | QDir("").mkdir(AvatarFactory::getManualDir());
|
---|
163 |
|
---|
164 | QFile source_file(file);
|
---|
165 | QFile target_file(getFileName());
|
---|
166 | if (source_file.open(IO_ReadOnly) && target_file.open(IO_WriteOnly)) {
|
---|
167 | // Copy all the data
|
---|
168 | char data[4096];
|
---|
169 | while (!source_file.atEnd()) {
|
---|
170 | Q_LONG read = source_file.readBlock(data,4096);
|
---|
171 | target_file.writeBlock(data,read);
|
---|
172 | }
|
---|
173 | }
|
---|
174 | }
|
---|
175 | }
|
---|
176 |
|
---|
177 | void FileAvatar::removeFromDisk()
|
---|
178 | {
|
---|
179 | QFile f(getFileName());
|
---|
180 | f.remove();
|
---|
181 | }
|
---|
182 |
|
---|
183 | bool FileAvatar::exists()
|
---|
184 | {
|
---|
185 | return QFileInfo(getFileName()).exists();
|
---|
186 | }
|
---|
187 |
|
---|
188 | QPixmap FileAvatar::getPixmap()
|
---|
189 | {
|
---|
190 | refresh();
|
---|
191 | return pixmap();
|
---|
192 | }
|
---|
193 |
|
---|
194 | void FileAvatar::refresh()
|
---|
195 | {
|
---|
196 | if (isDirty()) {
|
---|
197 | if (QFileInfo(getFileName()).exists()) {
|
---|
198 | QImage img(getFileName());
|
---|
199 | setImage(QImage(getFileName()));
|
---|
200 | }
|
---|
201 | else
|
---|
202 | resetImage();
|
---|
203 | }
|
---|
204 | }
|
---|
205 |
|
---|
206 |
|
---|
207 | QString FileAvatar::getFileName() const
|
---|
208 | {
|
---|
209 | QString f = getJid().bare();
|
---|
210 | f.replace('@',"_at_");
|
---|
211 | return QDir(AvatarFactory::getManualDir()).filePath(f);
|
---|
212 | }
|
---|
213 |
|
---|
214 |
|
---|
215 | bool FileAvatar::isDirty() const
|
---|
216 | {
|
---|
217 | return (pixmap().isNull()
|
---|
218 | || !QFileInfo(getFileName()).exists()
|
---|
219 | || QFileInfo(getFileName()).lastModified() > lastModified());
|
---|
220 | }
|
---|
221 |
|
---|
222 |
|
---|
223 | //------------------------------------------------------------------------------
|
---|
224 |
|
---|
225 | //------------------------------------------------------------------------------
|
---|
226 | // ClientAvatar: Avatar representing the logo of a specific client.
|
---|
227 | //------------------------------------------------------------------------------
|
---|
228 |
|
---|
229 | class ClientAvatar : public Avatar
|
---|
230 | {
|
---|
231 | public:
|
---|
232 | ClientAvatar(const QString& name);
|
---|
233 |
|
---|
234 | protected:
|
---|
235 | static ClientAvatar getClientAvatar(const QString& client);
|
---|
236 | static QMap<QString,ClientAvatar*> client_avatars;
|
---|
237 | };
|
---|
238 |
|
---|
239 |
|
---|
240 | ClientAvatar::ClientAvatar(const QString& name)
|
---|
241 | {
|
---|
242 | //printf("%s\n",name.latin1());
|
---|
243 | QPixmap p;
|
---|
244 | if (name == "Psi")
|
---|
245 | p = IconsetFactory::icon("psi/psiMac").pixmap();
|
---|
246 | else if (name == "gaim")
|
---|
247 | p = IconsetFactory::icon("client/gaim").pixmap();
|
---|
248 | else if (name == "Gabber")
|
---|
249 | p = IconsetFactory::icon("client/gabber").pixmap();
|
---|
250 | else if (name == "Gossip")
|
---|
251 | p = IconsetFactory::icon("client/gossip").pixmap();
|
---|
252 | else if (name == "Exodus")
|
---|
253 | p = IconsetFactory::icon("client/exodus").pixmap();
|
---|
254 | else if (name == "Pandion")
|
---|
255 | p = IconsetFactory::icon("client/pandion").pixmap();
|
---|
256 | else if (name == "Nitro")
|
---|
257 | p = IconsetFactory::icon("client/nitro").pixmap();
|
---|
258 | else if (name == "Kopete")
|
---|
259 | p = IconsetFactory::icon("client/kopete").pixmap();
|
---|
260 | else if (name == "Tkabber")
|
---|
261 | p = IconsetFactory::icon("client/tkabber").pixmap();
|
---|
262 | else if (name == "MSN Transport")
|
---|
263 | p = IconsetFactory::icon("client/msn").pixmap();
|
---|
264 | else if (name == "AIM Transport")
|
---|
265 | p = IconsetFactory::icon("client/aim").pixmap();
|
---|
266 |
|
---|
267 | // Clients with an ugly version string come last
|
---|
268 | else if (name.contains("Gadu-Gadu"))
|
---|
269 | p = IconsetFactory::icon("client/gadu").pixmap();
|
---|
270 | else if (name.contains("Miranda"))
|
---|
271 | p = IconsetFactory::icon("client/miranda").pixmap();
|
---|
272 | else if (name.contains("JAJC"))
|
---|
273 | p = IconsetFactory::icon("client/jajc").pixmap();
|
---|
274 | else if (name.contains("Trillian"))
|
---|
275 | p = IconsetFactory::icon("client/trillian").pixmap();
|
---|
276 | else if (name.contains("Yahoo"))
|
---|
277 | p = IconsetFactory::icon("client/yahoo").pixmap();
|
---|
278 | else if (name.contains("JIT"))
|
---|
279 | p = IconsetFactory::icon("client/icq").pixmap();
|
---|
280 | //else
|
---|
281 | // p = IconsetFactory::icon("client/unknown").pixmap();
|
---|
282 |
|
---|
283 | setImage(p);
|
---|
284 | }
|
---|
285 |
|
---|
286 |
|
---|
287 | //------------------------------------------------------------------------------
|
---|
288 |
|
---|
289 | //------------------------------------------------------------------------------
|
---|
290 | // Avatar factory
|
---|
291 | //------------------------------------------------------------------------------
|
---|
292 |
|
---|
293 | AvatarFactory::AvatarFactory(PsiAccount* pa) : pa_(pa)
|
---|
294 | {
|
---|
295 | VCardFactory::registerVCardChanged(this,SLOT(updateAvatar(const Jid&)));
|
---|
296 | }
|
---|
297 |
|
---|
298 |
|
---|
299 | QPixmap AvatarFactory::getAvatar(const Jid& jid, const QString& resource)
|
---|
300 | {
|
---|
301 | // Compute the avatar of the user
|
---|
302 | Avatar* av = retrieveAvatar(jid,resource);
|
---|
303 |
|
---|
304 | // If the avatar changed since the previous request, notify everybody of this
|
---|
305 | if (av != active_avatars_[jid.full()]) {
|
---|
306 | active_avatars_[jid.full()] = av;
|
---|
307 | active_avatars_[jid.bare()] = av;
|
---|
308 | emit avatarChanged(jid);
|
---|
309 | }
|
---|
310 |
|
---|
311 | return (av ? av->getPixmap() : QPixmap());
|
---|
312 | }
|
---|
313 |
|
---|
314 | Avatar* AvatarFactory::retrieveAvatar(const Jid& jid, const QString& client)
|
---|
315 | {
|
---|
316 | // printf("Retrieving avatar of %s\n", jid.full().latin1());
|
---|
317 |
|
---|
318 | // Try finding a file avatar.
|
---|
319 | //printf("File avatar\n");
|
---|
320 | if (!file_avatars_.contains(jid.bare())) {
|
---|
321 | //printf("File avatar not yet loaded\n");
|
---|
322 | file_avatars_[jid.bare()] = new FileAvatar(jid);
|
---|
323 | }
|
---|
324 | //printf("Trying file avatar\n");
|
---|
325 | if (!file_avatars_[jid.bare()]->isEmpty())
|
---|
326 | return file_avatars_[jid.bare()];
|
---|
327 |
|
---|
328 | // Try finding an avatar in the VCard
|
---|
329 | //printf("VCard avatar\n");
|
---|
330 | if (!vcard_avatars_.contains(jid.bare())) {
|
---|
331 | //printf("VCard avatar not yet loaded\n");
|
---|
332 | vcard_avatars_[jid.bare()] = new VCardAvatar(this, jid);
|
---|
333 | connect(vcard_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&)));
|
---|
334 | }
|
---|
335 | //printf("Trying VCard avatar\n");
|
---|
336 | if (!vcard_avatars_[jid.bare()]->isEmpty()) {
|
---|
337 | return vcard_avatars_[jid.bare()];
|
---|
338 | }
|
---|
339 |
|
---|
340 | // Return the client avatar
|
---|
341 | if (client.isEmpty()) {
|
---|
342 | return 0;
|
---|
343 | }
|
---|
344 | else {
|
---|
345 | if (!client_avatars_.contains(client)) {
|
---|
346 | // printf("Client avatar not yet loaded\n");
|
---|
347 | client_avatars_[client] = new ClientAvatar(client);
|
---|
348 | }
|
---|
349 | return client_avatars_[client];
|
---|
350 | }
|
---|
351 | }
|
---|
352 |
|
---|
353 | QPixmap AvatarFactory::getSelfAvatar()
|
---|
354 | {
|
---|
355 | return getAvatar(account()->jid(),"");
|
---|
356 | }
|
---|
357 |
|
---|
358 | void AvatarFactory::updateAvatar(const Jid& j)
|
---|
359 | {
|
---|
360 | // FIXME: Maybe we should really look up the client version
|
---|
361 | getAvatar(j,"");
|
---|
362 |
|
---|
363 | // FIXME: This signal might be emitted twice (first time from getAvatar()).
|
---|
364 | emit avatarChanged(j);
|
---|
365 | }
|
---|
366 |
|
---|
367 | void AvatarFactory::importManualAvatar(const Jid& j, const QString& fileName)
|
---|
368 | {
|
---|
369 | FileAvatar(j).import(fileName);
|
---|
370 | emit avatarChanged(j);
|
---|
371 | }
|
---|
372 |
|
---|
373 | void AvatarFactory::removeManualAvatar(const Jid& j)
|
---|
374 | {
|
---|
375 | FileAvatar(j).removeFromDisk();
|
---|
376 | // TODO: Remove from caches. Maybe create a clearManualAvatar() which
|
---|
377 | // removes the file but doesn't remove the avatar from caches (since it'll
|
---|
378 | // be created again whenever the FileAvatar is requested)
|
---|
379 | emit avatarChanged(j);
|
---|
380 | }
|
---|
381 |
|
---|
382 | bool AvatarFactory::hasManualAvatar(const Jid& j)
|
---|
383 | {
|
---|
384 | return FileAvatar(j).exists();
|
---|
385 | }
|
---|
386 |
|
---|
387 | QString AvatarFactory::getAvatarsDir()
|
---|
388 | {
|
---|
389 | QDir avatars(pathToProfile(activeProfile) + "/avatars");
|
---|
390 | if (!avatars.exists()) {
|
---|
391 | QDir profile(pathToProfile(activeProfile));
|
---|
392 | profile.mkdir("avatars");
|
---|
393 | }
|
---|
394 |
|
---|
395 | return avatars.path();
|
---|
396 | }
|
---|
397 |
|
---|
398 | QString AvatarFactory::getManualDir()
|
---|
399 | {
|
---|
400 | return getAvatarsDir() + "/manual";
|
---|
401 | }
|
---|
402 |
|
---|
403 | //------------------------------------------------------------------------------
|
---|
404 |
|
---|
405 | #include "avatars.moc"
|
---|