2 kopeteavatarmanager.cpp - Global avatar manager
4 Copyright (c) 2007 by Michaƫl Larouche <larouche@kde.org>
6 Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org>
8 *************************************************************************
10 * This library is free software; you can redistribute it and/or *
11 * modify it under the terms of the GNU Lesser General Public *
12 * License as published by the Free Software Foundation; either *
13 * version 2 of the License, or (at your option) any later version. *
15 *************************************************************************
17 #include "kopeteavatarmanager.h"
20 #include <QtCore/QLatin1String>
21 #include <QtCore/QBuffer>
22 #include <QtCore/QCoreApplication>
23 #include <QtCore/QFile>
24 #include <QtCore/QPointer>
25 #include <QtCore/QStringList>
26 #include <QtCore/QDir>
27 #include <QtGui/QPainter>
28 #include <QtGui/QImageReader>
32 #include <kstandarddirs.h>
37 #include <kio/netaccess.h>
41 #include <kopetecontact.h>
42 #include <kopeteaccount.h>
48 AvatarManager
*AvatarManager::s_self
= 0;
50 AvatarManager
*AvatarManager::self()
54 s_self
= new AvatarManager
;
59 class AvatarManager::Private
65 * Create directory if needed
66 * @param directory URL of the directory to create
68 void createDirectory(const KUrl
&directory
);
71 * Scale the given image to 96x96.
72 * @param source Original image
74 QImage
scaleImage(const QImage
&source
);
77 static const QString
UserDir("User");
78 static const QString
ContactDir("Contacts");
79 static const QString
AvatarConfig("avatarconfig.rc");
82 AvatarManager::AvatarManager(QObject
*parent
)
83 : QObject(parent
), d(new Private
)
85 // Locate avatar data dir on disk
86 d
->baseDir
= KUrl( KStandardDirs::locateLocal("appdata", QLatin1String("avatars") ) );
88 // Create directory on disk, if necessary
89 d
->createDirectory( d
->baseDir
);
92 AvatarManager::~AvatarManager()
97 Kopete::AvatarManager::AvatarEntry
AvatarManager::add(Kopete::AvatarManager::AvatarEntry newEntry
)
99 Q_ASSERT(!newEntry
.name
.isEmpty());
101 KUrl
avatarUrl(d
->baseDir
);
103 // First find where to save the file
104 switch(newEntry
.category
)
106 case AvatarManager::User
:
107 avatarUrl
.addPath( UserDir
);
108 d
->createDirectory( avatarUrl
);
110 case AvatarManager::Contact
:
111 avatarUrl
.addPath( ContactDir
);
112 d
->createDirectory( avatarUrl
);
113 // Use the account id for sub directory
114 if( newEntry
.contact
&& newEntry
.contact
->account() )
116 QString accountName
= newEntry
.contact
->account()->accountId();
117 avatarUrl
.addPath( accountName
);
118 d
->createDirectory( avatarUrl
);
125 KUrl
dataUrl(avatarUrl
);
127 kDebug(14010) << "Base directory: " << avatarUrl
.path();
129 // Second, open the avatar configuration in current directory.
130 KUrl configUrl
= avatarUrl
;
131 configUrl
.addPath( AvatarConfig
);
133 QByteArray data
= newEntry
.data
;
134 QImage avatar
= newEntry
.image
;
138 avatar
.loadFromData(data
);
140 else if(!newEntry
.dataPath
.isEmpty()){
141 QFile
f(newEntry
.dataPath
);
142 f
.open(QIODevice::ReadOnly
);
146 avatar
.loadFromData(data
);
148 else if(!avatar
.isNull()){
149 QByteArray tempArray
;
150 QBuffer
tempBuffer(&tempArray
);
151 tempBuffer
.open( QIODevice::WriteOnly
);
152 avatar
.save(&tempBuffer
, "PNG");
156 else if(!newEntry
.path
.isEmpty()){
157 avatar
= QImage(newEntry
.path
);
159 QFile
f(newEntry
.path
);
160 f
.open(QIODevice::ReadOnly
);
166 kDebug() << "Warning: No valid image source!";
170 avatar
= d
->scaleImage(avatar
);
172 QString avatarFilename
;
174 // for the contact avatar, save it with the contactId + .png
175 if (newEntry
.category
== AvatarManager::Contact
&& newEntry
.contact
)
177 avatarFilename
= newEntry
.contact
->contactId() + ".png";
181 // Find MD5 hash for filename
182 // MD5 always contain ASCII caracteres so it is more save for a filename.
183 // Name can use UTF-8 characters.
184 QByteArray tempArray
;
185 QBuffer
tempBuffer(&tempArray
);
186 tempBuffer
.open( QIODevice::WriteOnly
);
187 avatar
.save(&tempBuffer
, "PNG");
188 KMD5
context(tempArray
);
189 avatarFilename
= context
.hexDigest() + QLatin1String(".png");
192 // Save image on disk
193 kDebug(14010) << "Saving " << avatarFilename
<< " on disk.";
194 avatarUrl
.addPath( avatarFilename
);
196 if( !avatar
.save( avatarUrl
.path(), "PNG") )
198 kDebug(14010) << "Saving of " << avatarUrl
.path() << " failed !";
199 return AvatarEntry();
202 QString dataFilename
;
204 // for the contact avatar, save it with the contactId + .png
205 if (newEntry
.category
== AvatarManager::Contact
&& newEntry
.contact
)
207 dataFilename
= newEntry
.contact
->contactId() + QLatin1String("_");
210 dataFilename
+= KMD5(data
).hexDigest();
212 QBuffer
buffer(&data
);
213 buffer
.open(QIODevice::ReadOnly
);
214 QImageReader
ir(&buffer
);
215 dataFilename
+= QLatin1String(".") + QLatin1String(ir
.format());
217 // Save (original) data on disk
218 dataUrl
.addPath(dataFilename
);
219 QFile
f(dataUrl
.path());
220 if (!f
.open(QIODevice::WriteOnly
))
222 kDebug(14010) << "Saving of " << dataUrl
.path() << " failed !";
223 return AvatarEntry();
229 // Save metadata of image
230 KConfigGroup
avatarConfig(KSharedConfig::openConfig( configUrl
.path(), KConfig::SimpleConfig
), newEntry
.name
);
232 avatarConfig
.writeEntry( "Filename", avatarFilename
);
233 avatarConfig
.writeEntry( "DataFilename", dataFilename
);
234 avatarConfig
.writeEntry( "Category", int(newEntry
.category
) );
238 // Add final path to the new entry for avatarAdded signal
239 newEntry
.path
= avatarUrl
.path();
240 newEntry
.dataPath
= dataUrl
.path();
242 emit
avatarAdded(newEntry
);
247 bool AvatarManager::remove(Kopete::AvatarManager::AvatarEntry entryToRemove
)
249 // We need name and path to remove an avatar from the storage.
250 if( entryToRemove
.name
.isEmpty() && entryToRemove
.path
.isEmpty() )
253 // We don't allow removing avatars from Contact category
254 if( entryToRemove
.category
& Kopete::AvatarManager::Contact
)
257 // Delete the image file first, file delete is more likely to fail than config group remove.
258 if( KIO::NetAccess::del(KUrl(entryToRemove
.path
),0) )
260 kDebug(14010) << "Removing avatar from config.";
262 KUrl
configUrl(d
->baseDir
);
263 configUrl
.addPath( UserDir
);
264 configUrl
.addPath( AvatarConfig
);
266 KConfigGroup
avatarConfig ( KSharedConfig::openConfig( configUrl
.path(), KConfig::SimpleConfig
), entryToRemove
.name
);
267 avatarConfig
.deleteGroup();
270 emit
avatarRemoved(entryToRemove
);
278 void AvatarManager::Private::createDirectory(const KUrl
&directory
)
280 if( !QFile::exists(directory
.path()) )
282 kDebug(14010) << "Creating directory: " << directory
.path();
283 if( !KIO::NetAccess::mkdir(directory
,0) )
285 kDebug(14010) << "Directory " << directory
.path() <<" creating failed.";
290 QImage
AvatarManager::Private::scaleImage(const QImage
&source
)
297 //make an empty image and fill with transparent color
298 QImage
result(96, 96, QImage::Format_ARGB32
);
301 QPainter
paint(&result
);
304 // scale and center the image
305 if( source
.width() > 96 || source
.height() > 96 )
307 QImage scaled
= source
.scaled( 96, 96, Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
309 x
= (96 - scaled
.width()) / 2.0;
310 y
= (96 - scaled
.height()) / 2.0;
312 paint
.translate(x
, y
);
313 paint
.drawImage(QPoint(0, 0), scaled
);
315 x
= (96 - source
.width()) / 2.0;
316 y
= (96 - source
.height()) / 2.0;
318 paint
.translate(x
, y
);
319 paint
.drawImage(QPoint(0, 0), source
);
327 //BEGIN AvatarQueryJob
328 class AvatarQueryJob::Private
331 Private(AvatarQueryJob
*parent
)
332 : queryJob(parent
), category(AvatarManager::All
)
335 QPointer
<AvatarQueryJob
> queryJob
;
336 AvatarManager::AvatarCategory category
;
337 QList
<AvatarManager::AvatarEntry
> avatarList
;
340 void listAvatarDirectory(const QString
&path
);
343 AvatarQueryJob::AvatarQueryJob(QObject
*parent
)
344 : KJob(parent
), d(new Private(this))
349 AvatarQueryJob::~AvatarQueryJob()
354 void AvatarQueryJob::setQueryFilter(Kopete::AvatarManager::AvatarCategory category
)
356 d
->category
= category
;
359 void AvatarQueryJob::start()
361 d
->baseDir
= KUrl( KStandardDirs::locateLocal("appdata", QLatin1String("avatars") ) );
363 if( d
->category
& Kopete::AvatarManager::User
)
365 d
->listAvatarDirectory( UserDir
);
367 if( d
->category
& Kopete::AvatarManager::Contact
)
369 KUrl
contactUrl(d
->baseDir
);
370 contactUrl
.addPath( ContactDir
);
372 QDir
contactDir(contactUrl
.path());
373 QStringList subdirsList
= contactDir
.entryList( QDir::AllDirs
| QDir::NoDotAndDotDot
);
374 foreach(const QString
&subdir
, subdirsList
)
376 d
->listAvatarDirectory( ContactDir
+ QDir::separator() + subdir
);
384 QList
<Kopete::AvatarManager::AvatarEntry
> AvatarQueryJob::avatarList() const
386 return d
->avatarList
;
389 void AvatarQueryJob::Private::listAvatarDirectory(const QString
&relativeDirectory
)
391 KUrl avatarDirectory
= baseDir
;
392 avatarDirectory
.addPath(relativeDirectory
);
394 kDebug(14010) << "Listing avatars in " << avatarDirectory
.path();
396 // Look for Avatar configuration
397 KUrl avatarConfigUrl
= avatarDirectory
;
398 avatarConfigUrl
.addPath( AvatarConfig
);
399 if( QFile::exists(avatarConfigUrl
.path()) )
401 KConfig
*avatarConfig
= new KConfig( avatarConfigUrl
.path(), KConfig::SimpleConfig
);
402 // Each avatar entry in configuration is a group
403 QStringList groupEntryList
= avatarConfig
->groupList();
404 foreach(const QString
&groupEntry
, groupEntryList
)
406 KConfigGroup
cg(avatarConfig
, groupEntry
);
408 Kopete::AvatarManager::AvatarEntry listedEntry
;
409 listedEntry
.name
= groupEntry
;
410 listedEntry
.category
= static_cast<Kopete::AvatarManager::AvatarCategory
>( cg
.readEntry("Category", 0) );
412 QString filename
= cg
.readEntry( "Filename", QString() );
413 KUrl
avatarPath(avatarDirectory
);
414 avatarPath
.addPath( filename
);
415 listedEntry
.path
= avatarPath
.path();
417 QString dataFilename
= cg
.readEntry( "DataFilename", QString() );
418 KUrl
dataPath(avatarDirectory
);
419 dataPath
.addPath( dataFilename
);
420 listedEntry
.dataPath
= dataPath
.path();
422 avatarList
<< listedEntry
;
432 #include "kopeteavatarmanager.moc"