#include #include #include #include #include #include #include "xmpp.h" #include "avatars.h" #include "common.h" #include "iconset.h" #include "psiaccount.h" #include "profiles.h" #include "vcardfactory.h" #define MAX_AVATAR_SIZE 64 //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Avatar: Base class for avatars. //------------------------------------------------------------------------------ Avatar::Avatar() { } Avatar::~Avatar() { } int Avatar::maxSize() { return (option.avatarsSize > MAX_AVATAR_SIZE ? MAX_AVATAR_SIZE : option.avatarsSize); } void Avatar::setImage(const QImage& i) { if (i.width() > maxSize() || i.height() > option.avatarsSize) pixmap_.convertFromImage(i.smoothScale(maxSize(),maxSize(),QImage::ScaleMin)); else pixmap_.convertFromImage(i); } void Avatar::setImage(const QByteArray& ba) { setImage(QImage(ba)); } void Avatar::setImage(const QPixmap& p) { if (p.width() > maxSize() || p.height() > maxSize()) setImage(p.convertToImage()); else pixmap_ = p; } void Avatar::resetImage() { pixmap_ = QPixmap(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // VCardAvatar: Avatars coming from VCards of contacts. //------------------------------------------------------------------------------ class VCardAvatar : public QObject, public Avatar { Q_OBJECT public: VCardAvatar(AvatarFactory* factory, const Jid& jid); QPixmap getPixmap(); signals: void avatarChanged(const Jid&); private: Jid jid_; AvatarFactory* factory_; const VCard* lastVCard_; // Never dereference this, might point to dead object }; VCardAvatar::VCardAvatar(AvatarFactory* factory, const Jid& jid) : jid_(jid), factory_(factory) { const VCard* vcard = VCardFactory::vcard(jid_); if (vcard && vcard->photo()) { setImage(vcard->photo()); } lastVCard_ = vcard; } QPixmap VCardAvatar::getPixmap() { // Try to find an avatar if the current one is empty // FIXME: The pixmap shouldn't be updated by active polling, but by // some signal from the VCard factory when the vcard is updated. const VCard* vcard = VCardFactory::vcard(jid_); if (lastVCard_ != vcard) { if (vcard && vcard->photo()) { setImage(vcard->photo()); } else resetImage(); lastVCard_ = vcard; emit avatarChanged(jid_); } return pixmap(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // FileAvatar: Avatars coming from local files. //------------------------------------------------------------------------------ class FileAvatar : public Avatar { public: FileAvatar(const Jid& jid); void import(const QString& file); void removeFromDisk(); bool exists(); QPixmap getPixmap(); const Jid& getJid() const { return jid_; } protected: bool isDirty() const; QString getFileName() const; void refresh(); QDateTime lastModified() const { return lastModified_; } private: QDateTime lastModified_; Jid jid_; }; FileAvatar::FileAvatar(const Jid& jid) : jid_(jid) { } void FileAvatar::import(const QString& file) { if (QFileInfo(file).exists()) { // Check if the manual dir exists if (!QDir(AvatarFactory::getAvatarsDir()).exists()) QDir("").mkdir(AvatarFactory::getAvatarsDir()); if (!QDir(AvatarFactory::getManualDir()).exists()) QDir("").mkdir(AvatarFactory::getManualDir()); QFile source_file(file); QFile target_file(getFileName()); if (source_file.open(IO_ReadOnly) && target_file.open(IO_WriteOnly)) { // Copy all the data char data[4096]; while (!source_file.atEnd()) { Q_LONG read = source_file.readBlock(data,4096); target_file.writeBlock(data,read); } } } } void FileAvatar::removeFromDisk() { QFile f(getFileName()); f.remove(); } bool FileAvatar::exists() { return QFileInfo(getFileName()).exists(); } QPixmap FileAvatar::getPixmap() { refresh(); return pixmap(); } void FileAvatar::refresh() { if (isDirty()) { if (QFileInfo(getFileName()).exists()) { QImage img(getFileName()); setImage(QImage(getFileName())); } else resetImage(); } } QString FileAvatar::getFileName() const { QString f = getJid().bare(); f.replace('@',"_at_"); return QDir(AvatarFactory::getManualDir()).filePath(f); } bool FileAvatar::isDirty() const { return (pixmap().isNull() || !QFileInfo(getFileName()).exists() || QFileInfo(getFileName()).lastModified() > lastModified()); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // ClientAvatar: Avatar representing the logo of a specific client. //------------------------------------------------------------------------------ class ClientAvatar : public Avatar { public: ClientAvatar(const QString& name); protected: static ClientAvatar getClientAvatar(const QString& client); static QMap client_avatars; }; ClientAvatar::ClientAvatar(const QString& name) { //printf("%s\n",name.latin1()); QPixmap p; if (name == "Psi") p = IconsetFactory::icon("psi/psiMac").pixmap(); else if (name == "gaim") p = IconsetFactory::icon("client/gaim").pixmap(); else if (name == "Gabber") p = IconsetFactory::icon("client/gabber").pixmap(); else if (name == "Gossip") p = IconsetFactory::icon("client/gossip").pixmap(); else if (name == "Exodus") p = IconsetFactory::icon("client/exodus").pixmap(); else if (name == "Pandion") p = IconsetFactory::icon("client/pandion").pixmap(); else if (name == "Nitro") p = IconsetFactory::icon("client/nitro").pixmap(); else if (name == "Kopete") p = IconsetFactory::icon("client/kopete").pixmap(); else if (name == "Tkabber") p = IconsetFactory::icon("client/tkabber").pixmap(); else if (name == "MSN Transport") p = IconsetFactory::icon("client/msn").pixmap(); else if (name == "AIM Transport") p = IconsetFactory::icon("client/aim").pixmap(); // Clients with an ugly version string come last else if (name.contains("Gadu-Gadu")) p = IconsetFactory::icon("client/gadu").pixmap(); else if (name.contains("Miranda")) p = IconsetFactory::icon("client/miranda").pixmap(); else if (name.contains("JAJC")) p = IconsetFactory::icon("client/jajc").pixmap(); else if (name.contains("Trillian")) p = IconsetFactory::icon("client/trillian").pixmap(); else if (name.contains("Yahoo")) p = IconsetFactory::icon("client/yahoo").pixmap(); else if (name.contains("JIT")) p = IconsetFactory::icon("client/icq").pixmap(); //else // p = IconsetFactory::icon("client/unknown").pixmap(); setImage(p); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Avatar factory //------------------------------------------------------------------------------ AvatarFactory::AvatarFactory(PsiAccount* pa) : pa_(pa) { VCardFactory::registerVCardChanged(this,SLOT(updateAvatar(const Jid&))); } QPixmap AvatarFactory::getAvatar(const Jid& jid, const QString& resource) { // Compute the avatar of the user Avatar* av = retrieveAvatar(jid,resource); // If the avatar changed since the previous request, notify everybody of this if (av != active_avatars_[jid.full()]) { active_avatars_[jid.full()] = av; active_avatars_[jid.bare()] = av; emit avatarChanged(jid); } return (av ? av->getPixmap() : QPixmap()); } Avatar* AvatarFactory::retrieveAvatar(const Jid& jid, const QString& client) { // printf("Retrieving avatar of %s\n", jid.full().latin1()); // Try finding a file avatar. //printf("File avatar\n"); if (!file_avatars_.contains(jid.bare())) { //printf("File avatar not yet loaded\n"); file_avatars_[jid.bare()] = new FileAvatar(jid); } //printf("Trying file avatar\n"); if (!file_avatars_[jid.bare()]->isEmpty()) return file_avatars_[jid.bare()]; // Try finding an avatar in the VCard //printf("VCard avatar\n"); if (!vcard_avatars_.contains(jid.bare())) { //printf("VCard avatar not yet loaded\n"); vcard_avatars_[jid.bare()] = new VCardAvatar(this, jid); connect(vcard_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&))); } //printf("Trying VCard avatar\n"); if (!vcard_avatars_[jid.bare()]->isEmpty()) { return vcard_avatars_[jid.bare()]; } // Return the client avatar if (client.isEmpty()) { return 0; } else { if (!client_avatars_.contains(client)) { // printf("Client avatar not yet loaded\n"); client_avatars_[client] = new ClientAvatar(client); } return client_avatars_[client]; } } QPixmap AvatarFactory::getSelfAvatar() { return getAvatar(account()->jid(),""); } void AvatarFactory::updateAvatar(const Jid& j) { // FIXME: Maybe we should really look up the client version getAvatar(j,""); // FIXME: This signal might be emitted twice (first time from getAvatar()). emit avatarChanged(j); } void AvatarFactory::importManualAvatar(const Jid& j, const QString& fileName) { FileAvatar(j).import(fileName); emit avatarChanged(j); } void AvatarFactory::removeManualAvatar(const Jid& j) { FileAvatar(j).removeFromDisk(); // TODO: Remove from caches. Maybe create a clearManualAvatar() which // removes the file but doesn't remove the avatar from caches (since it'll // be created again whenever the FileAvatar is requested) emit avatarChanged(j); } bool AvatarFactory::hasManualAvatar(const Jid& j) { return FileAvatar(j).exists(); } QString AvatarFactory::getAvatarsDir() { QDir avatars(pathToProfile(activeProfile) + "/avatars"); if (!avatars.exists()) { QDir profile(pathToProfile(activeProfile)); profile.mkdir("avatars"); } return avatars.path(); } QString AvatarFactory::getManualDir() { return getAvatarsDir() + "/manual"; } //------------------------------------------------------------------------------ #include "avatars.moc"