/* * This file is part of the KDE libraries * Copyright (C) 2001-2002 Rolf Magnus * Copyright (C) 2001-2002 Carsten Pfeiffer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation version 2.0. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * $Id$ */ #include #include #include #include #include #include #include #include #include #include // needed for serialization of bool #include #include #include "tdefilemetainfo.h" // shared data of a KFileMetaInfoItem class KFileMetaInfoItem::Data : public TQShared { public: Data( const KFileMimeTypeInfo::ItemInfo* mti, const TQString& _key, const TQVariant& _value ) : TQShared(), mimeTypeInfo( mti ), key( _key ), value( _value ), dirty( false ), added( false ), removed( false ) {} // we use this one for the streaming operators Data() : mimeTypeInfo( 0L ) {} ~Data() { if ( this == null ) // only the null item owns its mimeTypeInfo delete mimeTypeInfo; } const KFileMimeTypeInfo::ItemInfo* mimeTypeInfo; // mimeTypeInfo has the key, too, but only for non-variable ones TQString key; TQVariant value; bool dirty :1; bool added :1; bool removed :1; static Data* null; static Data* makeNull(); }; //this is our null data KFileMetaInfoItem::Data* KFileMetaInfoItem::Data::null = 0L; static KStaticDeleter sd_KFileMetaInfoItemData; KFileMetaInfoItem::Data* KFileMetaInfoItem::Data::makeNull() { if (!null) { // We deliberately do not reset "null" after it has been destroyed! // Otherwise we will run into problems later in ~KFileMetaInfoItem // where the d-pointer is compared against null. KFileMimeTypeInfo::ItemInfo* info = new KFileMimeTypeInfo::ItemInfo(); null = new Data(info, TQString::null, TQVariant()); sd_KFileMetaInfoItemData.setObject( null ); } return null; } KFileMetaInfoItem::KFileMetaInfoItem( const KFileMimeTypeInfo::ItemInfo* mti, const TQString& key, const TQVariant& value ) : d( new Data( mti, key, value ) ) { } KFileMetaInfoItem::KFileMetaInfoItem( const KFileMetaInfoItem& item ) { // operator= does everything that's necessary d = Data::makeNull(); *this = item; } KFileMetaInfoItem::KFileMetaInfoItem() { d = Data::makeNull(); } KFileMetaInfoItem::~KFileMetaInfoItem() { deref(); } const KFileMetaInfoItem& KFileMetaInfoItem::operator= (const KFileMetaInfoItem & item ) { if (d != item.d) { // first deref the old one deref(); d = item.d; // and now ref the new one ref(); } return *this; } bool KFileMetaInfoItem::setValue( const TQVariant& value ) { // We don't call makeNull here since it isn't necassery, see deref() if ( d == Data::null ) return false; if ( ! (d->mimeTypeInfo->attributes() & KFileMimeTypeInfo::Modifiable ) || ! (value.canCast(d->mimeTypeInfo->type()))) { kdDebug(7033) << "setting the value of " << key() << "failed\n"; return false; } // kdDebug(7033) << key() << ".setValue()\n"; if ( d->value == value ) return true; d->dirty = true; d->value = value; // If we don't cast (and test for canCast in the above if), TQVariant is // very picky about types (e.g. TQString vs. TQCString or int vs. uint) d->value.cast(d->mimeTypeInfo->type()); return true; } bool KFileMetaInfoItem::isRemoved() const { return d->removed; } TQString KFileMetaInfoItem::key() const { return d->key; } TQString KFileMetaInfoItem::translatedKey() const { // are we a variable key? if (d->mimeTypeInfo->key().isNull()) { // then try if we have luck with i18n() return i18n(d->key.utf8()); } return d->mimeTypeInfo->translatedKey(); } const TQVariant& KFileMetaInfoItem::value() const { return d->value; } TQString KFileMetaInfoItem::string( bool mangle ) const { return d->mimeTypeInfo->string(d->value, mangle); } TQVariant::Type KFileMetaInfoItem::type() const { return d->mimeTypeInfo->type(); } uint KFileMetaInfoItem::unit() const { return d->mimeTypeInfo->unit(); } bool KFileMetaInfoItem::isModified() const { return d->dirty; } TQString KFileMetaInfoItem::prefix() const { return d->mimeTypeInfo->prefix(); } TQString KFileMetaInfoItem::suffix() const { return d->mimeTypeInfo->suffix(); } uint KFileMetaInfoItem::hint() const { return d->mimeTypeInfo->hint(); } uint KFileMetaInfoItem::attributes() const { return d->mimeTypeInfo->attributes(); } bool KFileMetaInfoItem::isEditable() const { return d->mimeTypeInfo->attributes() & KFileMimeTypeInfo::Modifiable; } bool KFileMetaInfoItem::isValid() const { // We don't call makeNull here since it isn't necassery: // If d is equal to null it means that null is initialized already. // null is 0L when it hasn't been initialized and d is never 0L. return d != Data::null; } void KFileMetaInfoItem::setAdded() { d->added = true; } void KFileMetaInfoItem::setRemoved() { d->removed = true; } void KFileMetaInfoItem::ref() { if (d != Data::null) d->ref(); } void KFileMetaInfoItem::deref() { // We don't call makeNull here since it isn't necassery: // If d is equal to null it means that null is initialized already. // null is 0L when it hasn't been initialized and d is never 0L. if ((d != Data::null) && d->deref()) { // kdDebug(7033) << "item " << d->key // << " is finally deleted\n"; delete d; d = 0; } } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // shared data of a KFileMetaInfo class KFileMetaInfo::Data : public TQShared { public: Data(const KURL& _url, uint _what) : TQShared(), url(_url), what(_what), mimeTypeInfo( 0L ) {} // wee use this one for the streaming operators Data() {}; KURL url; uint what; TQMap groups; const KFileMimeTypeInfo* mimeTypeInfo; TQStringList removedGroups; static Data* null; static Data* makeNull(); }; KFileMetaInfo::KFileMetaInfo( const TQString& path, const TQString& mimeType, uint what ) { KURL u; u.setPath(path); init(u, mimeType, what); } KFileMetaInfo::KFileMetaInfo( const KURL& url, const TQString& mimeType, uint what ) { init(url, mimeType, what); } void KFileMetaInfo::init( const KURL& url, const TQString& mimeType, uint what ) { d = new Data( url, what ); TQString mT; if (mimeType.isEmpty()) mT = KMimeType::findByURL(url)->name(); else mT = mimeType; // let's "share our property" KFileMetaInfo item(*this); //kdDebug() << k_funcinfo << mT << " " << url << endl; d->mimeTypeInfo = KFileMetaInfoProvider::self()->mimeTypeInfo( mT, url.protocol() ); if ( d->mimeTypeInfo ) { //kdDebug(7033) << "Found mimetype info for " << mT /* or protocol*/ << endl; KFilePlugin *p = plugin(); Q_ASSERT( p ); if ( p && !p->readInfo( item, what) ) { deref(); d = Data::makeNull(); } } else { // kdDebug(7033) << "No mimetype info for " << mimeType << endl; deref(); d = Data::makeNull(); } } KFileMetaInfo::KFileMetaInfo( const KFileMetaInfo& original ) { // operator= does everything that's necessary d = Data::makeNull(); *this = original; } KFileMetaInfo::KFileMetaInfo() { d = Data::makeNull(); } KFileMetaInfo::~KFileMetaInfo() { deref(); } TQStringList KFileMetaInfo::supportedGroups() const { assert(isValid()); return d->mimeTypeInfo->supportedGroups(); } TQStringList KFileMetaInfo::supportedKeys() const { assert(isValid()); return d->mimeTypeInfo->supportedKeys(); } TQStringList KFileMetaInfo::groups() const { TQStringList list; TQMapConstIterator it = d->groups.begin(); for ( ; it != d->groups.end(); ++it ) list += (*it).name(); return list; } TQStringList KFileMetaInfo::editableGroups() const { TQStringList list; TQStringList supported = supportedGroups(); TQStringList::ConstIterator it = supported.begin(); for ( ; it != supported.end(); ++it ) { const KFileMimeTypeInfo::GroupInfo * groupInfo = d->mimeTypeInfo->groupInfo( *it ); if ( groupInfo && groupInfo->attributes() & (KFileMimeTypeInfo::Addable | KFileMimeTypeInfo::Removable) ) list.append( *it ); } return list; } TQStringList KFileMetaInfo::preferredGroups() const { assert(isValid()); TQStringList list = groups(); TQStringList newlist; TQStringList preferred = d->mimeTypeInfo->preferredGroups(); TQStringList::Iterator pref; // move all keys from the preferred groups that are in our list to a new list for ( pref = preferred.begin(); pref != preferred.end(); ++pref ) { TQStringList::Iterator group = list.find(*pref); if ( group != list.end() ) { newlist.append( *group ); list.remove(group); } } // now the old list only contains the non-preferred items, so we // add the remaining ones to newlist newlist += list; return newlist; } TQStringList KFileMetaInfo::preferredKeys() const { TQStringList newlist; TQStringList list = preferredGroups(); for (TQStringList::Iterator git = list.begin(); git != list.end(); ++git) { newlist += d->groups[*git].preferredKeys(); } return newlist; } KFileMetaInfoGroup KFileMetaInfo::group(const TQString& key) const { TQMapIterator it = d->groups.find( key ); if ( it != d->groups.end() ) return it.data(); else return KFileMetaInfoGroup(); } bool KFileMetaInfo::addGroup( const TQString& name ) { assert(isValid()); if ( d->mimeTypeInfo->supportedGroups().contains(name) && ! d->groups.contains(name) ) { KFileMetaInfoGroup group( name, d->mimeTypeInfo ); // add all the items that can't be added by the user later const KFileMimeTypeInfo::GroupInfo* ginfo = d->mimeTypeInfo->groupInfo(name); Q_ASSERT(ginfo); if (!ginfo) return false; TQStringList keys = ginfo->supportedKeys(); for (TQStringList::Iterator it = keys.begin(); it != keys.end(); ++it) { const KFileMimeTypeInfo::ItemInfo* iteminfo = ginfo->itemInfo(*it); Q_ASSERT(ginfo); if (!iteminfo) return false; if ( !(iteminfo->attributes() & KFileMimeTypeInfo::Addable) && (iteminfo->attributes() & KFileMimeTypeInfo::Modifiable)) { // append it now or never group.appendItem(iteminfo->key(), TQVariant()); } } d->groups.insert(name, group); group.setAdded(); return true; } return false; } bool KFileMetaInfo::removeGroup( const TQString& name ) { TQMapIterator it = d->groups.find(name); if ( (it==d->groups.end()) || !((*it).attributes() & KFileMimeTypeInfo::Removable)) return false; d->groups.remove(it); d->removedGroups.append(name); return true; } TQStringList KFileMetaInfo::removedGroups() { return d->removedGroups; } const KFileMetaInfo& KFileMetaInfo::operator= (const KFileMetaInfo& info ) { if (d != info.d) { deref(); // first deref the old one d = info.d; // and now ref the new one ref(); } return *this; } bool KFileMetaInfo::isValid() const { // We don't call makeNull here since it isn't necassery, see deref() return d != Data::null; } bool KFileMetaInfo::isEmpty() const { for (TQMapIterator it = d->groups.begin(); it!=d->groups.end(); ++it) if (!(*it).isEmpty()) return false; return true; } bool KFileMetaInfo::applyChanges() { return applyChanges( path() ); } bool KFileMetaInfo::applyChanges( const TQString& path ) { bool doit = false; // kdDebug(7033) << "KFileMetaInfo::applyChanges()\n"; // look up if we need to write to the file TQMapConstIterator it; for (it = d->groups.begin(); it!=d->groups.end() && !doit; ++it) { if ( (*it).isModified() ) doit = true; else { TQStringList keys = it.data().keys(); for (TQStringList::Iterator it2 = keys.begin(); it2!=keys.end(); ++it2) { if ( (*it)[*it2].isModified() ) { doit = true; break; } } } } if (!doit) { kdDebug(7033) << "Don't need to write, nothing changed\n"; return true; } KFilePlugin* p = plugin(); if (!p) return false; // kdDebug(7033) << "Ok, trying to write the info\n"; KURL savedURL = url(); d->url = KURL(); d->url.setPath( path ); bool ret = p->writeInfo(*this); d->url = savedURL; return ret; } KFilePlugin * KFileMetaInfo::plugin() const { assert(isValid()); KFileMetaInfoProvider* prov = KFileMetaInfoProvider::self(); return prov->plugin( d->mimeTypeInfo->mimeType(), d->url.protocol() ); } TQString KFileMetaInfo::mimeType() const { assert(isValid()); return d->mimeTypeInfo->mimeType(); } bool KFileMetaInfo::contains(const TQString& key) const { TQStringList glist = groups(); for (TQStringList::Iterator it = glist.begin(); it != glist.end(); ++it) { KFileMetaInfoGroup g = d->groups[*it]; if (g.contains(key)) return true; } return false; } bool KFileMetaInfo::containsGroup(const TQString& key) const { return groups().contains(key); } KFileMetaInfoItem KFileMetaInfo::item( const TQString& key) const { TQStringList groups = preferredGroups(); for (TQStringList::Iterator it = groups.begin(); it != groups.end(); ++it) { KFileMetaInfoItem i = d->groups[*it][key]; if (i.isValid()) return i; } return KFileMetaInfoItem(); } KFileMetaInfoItem KFileMetaInfo::item(const KFileMetaInfoItem::Hint hint) const { TQStringList groups = preferredGroups(); TQStringList::ConstIterator it; for (it = groups.begin(); it != groups.end(); ++it) { KFileMetaInfoItem i = d->groups[*it].item(hint); if (i.isValid()) return i; } return KFileMetaInfoItem(); } KFileMetaInfoItem KFileMetaInfo::saveItem( const TQString& key, const TQString& preferredGroup, bool createGroup ) { assert(isValid()); // try the preferred groups first if ( !preferredGroup.isEmpty() ) { TQMapIterator it = d->groups.find( preferredGroup ); // try to create the preferred group, if necessary if ( it == d->groups.end() && createGroup ) { const KFileMimeTypeInfo::GroupInfo *groupInfo = d->mimeTypeInfo->groupInfo( preferredGroup ); if ( groupInfo && groupInfo->supportedKeys().contains( key ) ) { if ( addGroup( preferredGroup ) ) it = d->groups.find( preferredGroup ); } } if ( it != d->groups.end() ) { KFileMetaInfoItem item = it.data().addItem( key ); if ( item.isValid() ) return item; } } TQStringList groups = preferredGroups(); KFileMetaInfoItem item; TQStringList::ConstIterator groupIt = groups.begin(); for ( ; groupIt != groups.end(); ++groupIt ) { TQMapIterator it = d->groups.find( *groupIt ); if ( it != d->groups.end() ) { KFileMetaInfoGroup group = it.data(); item = findEditableItem( group, key ); if ( item.isValid() ) return item; } else // not existant -- try to create the group { const KFileMimeTypeInfo::GroupInfo *groupInfo = d->mimeTypeInfo->groupInfo( *groupIt ); if ( groupInfo && groupInfo->supportedKeys().contains( key ) ) { if ( addGroup( *groupIt ) ) { KFileMetaInfoGroup group = d->groups[*groupIt]; KFileMetaInfoItem item = group.addItem( key ); if ( item.isValid() ) return item; // else ### add when removeGroup() is implemented :) // removeGroup( *groupIt ); // couldn't add item -> remove } } } } // finally check for variable items return item; } KFileMetaInfoItem KFileMetaInfo::findEditableItem( KFileMetaInfoGroup& group, const TQString& key ) { assert(isValid()); KFileMetaInfoItem item = group.addItem( key ); if ( item.isValid() && item.isEditable() ) return item; if ( (d->mimeTypeInfo->groupInfo( group.name() )->attributes() & KFileMimeTypeInfo::Addable) ) return item; return KFileMetaInfoItem(); } KFileMetaInfoGroup KFileMetaInfo::appendGroup(const TQString& name) { assert(isValid()); if ( d->mimeTypeInfo->supportedGroups().contains(name) && ! d->groups.contains(name) ) { KFileMetaInfoGroup group( name, d->mimeTypeInfo ); d->groups.insert(name, group); return group; } else { kdWarning(7033) << "Someone's trying to add a KFileMetaInfoGroup which is not supported or already existing: " << name << endl; return KFileMetaInfoGroup(); } } TQString KFileMetaInfo::path() const { return d->url.isLocalFile() ? d->url.path() : TQString::null; } KURL KFileMetaInfo::url() const { return d->url; } void KFileMetaInfo::ref() { if (d != Data::null) d->ref(); } void KFileMetaInfo::deref() { // We don't call makeNull here since it isn't necassery: // If d is equal to null it means that null is initialized already. // null is 0L when it hasn't been initialized and d is never 0L. if ((d != Data::null) && d->deref()) { // kdDebug(7033) << "metainfo object for " << d->url.path << " is finally deleted\n"; delete d; d = 0; } } KFileMetaInfo::Data* KFileMetaInfo::Data::null = 0L; static KStaticDeleter sd_KFileMetaInfoData; KFileMetaInfo::Data* KFileMetaInfo::Data::makeNull() { if (!null) // We deliberately do not reset "null" after it has been destroyed! // Otherwise we will run into problems later in ~KFileMetaInfoItem // where the d-pointer is compared against null. null = sd_KFileMetaInfoData.setObject( new KFileMetaInfo::Data(KURL(), 0) ); return null; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// KFilePlugin::KFilePlugin( TQObject *parent, const char *name, const TQStringList& /*args*/) : TQObject( parent, name ) { // kdDebug(7033) << "loaded a plugin for " << name << endl; } KFilePlugin::~KFilePlugin() { // kdDebug(7033) << "unloaded a plugin for " << name() << endl; } KFileMimeTypeInfo * KFilePlugin::addMimeTypeInfo( const TQString& mimeType ) { return KFileMetaInfoProvider::self()->addMimeTypeInfo( mimeType ); } void KFilePlugin::virtual_hook( int, void* ) { /*BASE::virtual_hook( id, data );*/ } KFileMimeTypeInfo::GroupInfo* KFilePlugin::addGroupInfo(KFileMimeTypeInfo* info, const TQString& key, const TQString& translatedKey) const { return info->addGroupInfo(key, translatedKey); } void KFilePlugin::setAttributes(KFileMimeTypeInfo::GroupInfo* gi, uint attr) const { gi->m_attr = attr; } void KFilePlugin::addVariableInfo(KFileMimeTypeInfo::GroupInfo* gi, TQVariant::Type type, uint attr) const { gi->addVariableInfo(type, attr); } KFileMimeTypeInfo::ItemInfo* KFilePlugin::addItemInfo(KFileMimeTypeInfo::GroupInfo* gi, const TQString& key, const TQString& translatedKey, TQVariant::Type type) { return gi->addItemInfo(key, translatedKey, type); } void KFilePlugin::setAttributes(KFileMimeTypeInfo::ItemInfo* item, uint attr) { item->m_attr = attr; } void KFilePlugin::setHint(KFileMimeTypeInfo::ItemInfo* item, uint hint) { item->m_hint = hint; } void KFilePlugin::setUnit(KFileMimeTypeInfo::ItemInfo* item, uint unit) { item->m_unit = unit; // set prefix and suffix switch (unit) { case KFileMimeTypeInfo::Seconds: item->m_suffix = i18n("s"); break; case KFileMimeTypeInfo::MilliSeconds: item->m_suffix = i18n("ms"); break; case KFileMimeTypeInfo::BitsPerSecond: item->m_suffix = i18n("bps"); break; case KFileMimeTypeInfo::Pixels: item->m_suffix = i18n("pixels"); break; case KFileMimeTypeInfo::Inches: item->m_suffix = i18n("in"); break; case KFileMimeTypeInfo::Centimeters: item->m_suffix = i18n("cm"); break; case KFileMimeTypeInfo::Bytes: item->m_suffix = i18n("B"); break; case KFileMimeTypeInfo::KiloBytes: item->m_suffix = i18n("KB"); break; case KFileMimeTypeInfo::FramesPerSecond: item->m_suffix = i18n("fps"); break; case KFileMimeTypeInfo::DotsPerInch: item->m_suffix = i18n("dpi"); break; case KFileMimeTypeInfo::BitsPerPixel: item->m_suffix = i18n("bpp"); break; case KFileMimeTypeInfo::Hertz: item->m_suffix = i18n("Hz"); break; case KFileMimeTypeInfo::Millimeters: item->m_suffix = i18n("mm"); } } void KFilePlugin::setPrefix(KFileMimeTypeInfo::ItemInfo* item, const TQString& prefix) { item->m_prefix = prefix; } void KFilePlugin::setSuffix(KFileMimeTypeInfo::ItemInfo* item, const TQString& suffix) { item->m_suffix = suffix; } KFileMetaInfoGroup KFilePlugin::appendGroup(KFileMetaInfo& info, const TQString& key) { return info.appendGroup(key); } void KFilePlugin::appendItem(KFileMetaInfoGroup& group, const TQString& key, TQVariant value) { group.appendItem(key, value); } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// KFileMetaInfoProvider * KFileMetaInfoProvider::s_self; static KStaticDeleter sd; KFileMetaInfoProvider * KFileMetaInfoProvider::self() { if ( !s_self ) s_self = sd.setObject( s_self, new KFileMetaInfoProvider() ); return s_self; } KFileMetaInfoProvider::KFileMetaInfoProvider() { m_plugins.setAutoDelete( true ); } KFileMetaInfoProvider::~KFileMetaInfoProvider() { m_plugins.clear(); sd.setObject( 0 ); } KFilePlugin* KFileMetaInfoProvider::loadPlugin( const TQString& mimeType, const TQString& protocol ) { //kdDebug() << "loadPlugin: mimeType=" << mimeType << " protocol=" << protocol << endl; // Currently the idea is: either the mimetype is set or the protocol, but not both. // We need PNG fileinfo, and trash: fileinfo, but not "PNG in the trash". TQString queryMimeType, query; if ( !mimeType.isEmpty() ) { query = "(not exist [X-TDE-Protocol])"; queryMimeType = mimeType; } else { query = TQString::fromLatin1( "[X-TDE-Protocol] == '%1'" ).arg(protocol); // querying for a protocol: we have no mimetype, so we need to use KFilePlugin as one queryMimeType = "KFilePlugin"; // hopefully using KFilePlugin as genericMimeType too isn't a problem } const TDETrader::OfferList offers = TDETrader::self()->query( queryMimeType, "KFilePlugin", query, TQString::null ); if ( offers.isEmpty() ) return 0; KService::Ptr service = *(offers.begin()); Q_ASSERT( service && service->isValid() ); if ( !service || !service->isValid() ) return 0; KFilePlugin* plugin = KParts::ComponentFactory::createInstanceFromService ( service, TQT_TQOBJECT(this), mimeType.local8Bit() ); if (!plugin) kdWarning(7033) << "error loading the plugin from " << service->desktopEntryPath() << endl; return plugin; } KFilePlugin* KFileMetaInfoProvider::loadAndRegisterPlugin( const TQString& mimeType, const TQString& protocol ) { Q_ASSERT( m_pendingMimetypeInfos.isEmpty() ); m_pendingMimetypeInfos.clear(); KFilePlugin* plugin = loadPlugin( mimeType, protocol ); if ( !plugin ) { // No plugin found. Remember that, to save time. m_plugins.insert( protocol.isEmpty() ? mimeType : protocol, new CachedPluginInfo ); return 0; } if ( !protocol.isEmpty() ) { // Protocol-metainfo: only one entry Q_ASSERT( m_pendingMimetypeInfos.count() == 1 ); KFileMimeTypeInfo* info = m_pendingMimetypeInfos[ protocol ]; Q_ASSERT( info ); m_plugins.insert( protocol, new CachedPluginInfo( plugin, info, true ) ); } else { // Mimetype-metainfo: the plugin can register itself for multiple mimetypes, remember them all bool first = true; TQDictIterator it( m_pendingMimetypeInfos ); for( ; it.current(); ++it ) { KFileMimeTypeInfo* info = it.current(); m_plugins.insert( it.currentKey(), new CachedPluginInfo( plugin, info, first ) ); first = false; } // Hopefully the above includes the mimetype we asked for! if ( m_pendingMimetypeInfos.find( mimeType ) == 0 ) kdWarning(7033) << plugin->className() << " was created for " << mimeType << " but doesn't call addMimeTypeInfo for it!" << endl; } m_pendingMimetypeInfos.clear(); return plugin; } KFilePlugin * KFileMetaInfoProvider::plugin(const TQString& mimeType) { return plugin( mimeType, TQString::null ); } KFilePlugin * KFileMetaInfoProvider::plugin(const TQString& mimeType, const TQString& protocol) { //kdDebug(7033) << "plugin() : looking for plugin for protocol=" << protocol << " mimeType=" << mimeType << endl; if ( !protocol.isEmpty() ) { CachedPluginInfo *cache = m_plugins.find( protocol ); if ( cache && cache->plugin ) { return cache->plugin; } if ( !cache ) { KFilePlugin* plugin = loadAndRegisterPlugin( TQString::null, protocol ); if ( plugin ) return plugin; } } CachedPluginInfo *cache = m_plugins.find( mimeType ); if ( cache ) { return cache->plugin; } KFilePlugin* plugin = loadAndRegisterPlugin( mimeType, TQString::null ); #if 0 kdDebug(7033) << "currently loaded plugins:\n"; TQDictIterator it( m_plugins ); for( ; it.current(); ++it ) { CachedPluginInfo* cache = it.current(); kdDebug(7033) << it.currentKey() // mimetype or protocol << " : " << (cache->plugin ? cache->plugin->className() : "(no plugin)") << endl; // plugin // TODO print cache->mimeTypeInfo } #endif return plugin; } TQStringList KFileMetaInfoProvider::preferredKeys( const TQString& mimeType ) const { KService::Ptr service = KServiceTypeProfile::preferredService( mimeType, "KFilePlugin"); if ( !service || !service->isValid() ) { // kdDebug(7033) << "no valid service found\n"; return TQStringList(); } return service->property("PreferredItems").toStringList(); } TQStringList KFileMetaInfoProvider::preferredGroups( const TQString& mimeType ) const { KService::Ptr service = KServiceTypeProfile::preferredService( mimeType, "KFilePlugin"); if ( !service || !service->isValid() ) { // kdDebug(7033) << "no valid service found\n"; return TQStringList(); } return service->property("PreferredGroups").toStringList(); } const KFileMimeTypeInfo * KFileMetaInfoProvider::mimeTypeInfo( const TQString& mimeType ) { return mimeTypeInfo( mimeType, TQString::null ); } const KFileMimeTypeInfo * KFileMetaInfoProvider::mimeTypeInfo( const TQString& mimeType, const TQString& protocol ) { //kdDebug(7033) << "mimeTypeInfo() : looking for plugin for protocol=" << protocol << " mimeType=" << mimeType << endl; if ( !protocol.isEmpty() ) { CachedPluginInfo *cache = m_plugins.find( protocol ); if ( cache && cache->mimeTypeInfo ) { return cache->mimeTypeInfo; } if ( !cache ) { loadAndRegisterPlugin( TQString::null, protocol ); cache = m_plugins.find( protocol ); if ( cache && cache->mimeTypeInfo ) { return cache->mimeTypeInfo; } } } CachedPluginInfo *cache = m_plugins.find( mimeType ); if ( cache ) { return cache->mimeTypeInfo; } loadAndRegisterPlugin( mimeType, TQString::null ); cache = m_plugins.find( mimeType ); if ( cache ) { return cache->mimeTypeInfo; } return 0; } KFileMimeTypeInfo * KFileMetaInfoProvider::addMimeTypeInfo( const TQString& mimeType ) { KFileMimeTypeInfo *info = m_pendingMimetypeInfos.find( mimeType ); Q_ASSERT( !info ); if ( !info ) { info = new KFileMimeTypeInfo( mimeType ); m_pendingMimetypeInfos.insert( mimeType, info ); } info->m_preferredKeys = preferredKeys( mimeType ); info->m_preferredGroups = preferredGroups( mimeType ); return info; } TQStringList KFileMetaInfoProvider::supportedMimeTypes() const { TQStringList allMimeTypes; TQString tdefilePlugin = "KFilePlugin"; TDETrader::OfferList offers = TDETrader::self()->query( "KFilePlugin" ); TDETrader::OfferListIterator it = offers.begin(); for ( ; it != offers.end(); ++it ) { const TQStringList mimeTypes = (*it)->serviceTypes(); TQStringList::ConstIterator it2 = mimeTypes.begin(); for ( ; it2 != mimeTypes.end(); ++it2 ) if ( allMimeTypes.find( *it2 ) == allMimeTypes.end() && *it2 != tdefilePlugin ) // also in serviceTypes() allMimeTypes.append( *it2 ); } return allMimeTypes; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // shared data of a KFileMetaInfoGroup class KFileMetaInfoGroup::Data : public TQShared { public: Data(const TQString& _name) : TQShared(), name(_name), mimeTypeInfo(0L), dirty( false ), added( false ) {} // we use this one for the streaming operators Data() : mimeTypeInfo(0L) {} ~Data() { if ( this == null ) delete mimeTypeInfo; }; TQString name; TQMap items; const KFileMimeTypeInfo* mimeTypeInfo; TQStringList removedItems; bool dirty :1; bool added :1; static Data* null; static Data* makeNull(); }; KFileMetaInfoGroup::KFileMetaInfoGroup( const TQString& name, const KFileMimeTypeInfo* info ) : d(new Data( name ) ) { d->mimeTypeInfo = info; } KFileMetaInfoGroup::KFileMetaInfoGroup( const KFileMetaInfoGroup& original ) { // operator= does everything that's necessary d = Data::makeNull(); *this = original; } KFileMetaInfoGroup::KFileMetaInfoGroup() { d = Data::makeNull(); } KFileMetaInfoGroup::~KFileMetaInfoGroup() { deref(); } const KFileMetaInfoGroup& KFileMetaInfoGroup::operator= (const KFileMetaInfoGroup& info ) { if (d != info.d) { deref(); // first deref the old one d = info.d; // and now ref the new one ref(); } return *this; } bool KFileMetaInfoGroup::isValid() const { // We don't call makeNull here since it isn't necassery, see deref() return d != Data::null; } bool KFileMetaInfoGroup::isEmpty() const { return d->items.isEmpty(); } TQStringList KFileMetaInfoGroup::preferredKeys() const { assert(isValid()); TQStringList list = keys(); TQStringList newlist; TQStringList preferredKeys = d->mimeTypeInfo->preferredKeys(); TQStringList::Iterator pref; TQStringList::Iterator begin = preferredKeys.begin(); TQStringList::Iterator end = preferredKeys.end(); // move all keys from the preferred keys that are in our list to a new list for ( pref = begin; pref!=end; ++pref ) { TQStringList::Iterator item = list.find(*pref); if ( item != list.end() ) { newlist.append( *item ); list.remove(item); } } // now the old list only contains the non-preferred items, so we // add the remaining ones to newlist newlist += list; return newlist; } TQStringList KFileMetaInfoGroup::keys() const { if (d == Data::makeNull()) kdWarning(7033) << "attempt to get the keys of " "an invalid metainfo group"; TQStringList list; // make a TQStringList with all available keys TQMapConstIterator it; for (it = d->items.begin(); it!=d->items.end(); ++it) { list.append(it.data().key()); // kdDebug(7033) << "Item " << it.data().key() << endl; } return list; } TQString KFileMetaInfoGroup::translatedName() const { assert(isValid()); return d->mimeTypeInfo->groupInfo(d->name)->translatedName(); } TQStringList KFileMetaInfoGroup::supportedKeys() const { assert(isValid()); return d->mimeTypeInfo->groupInfo(d->name)->supportedKeys(); } bool KFileMetaInfoGroup::supportsVariableKeys() const { assert(isValid()); return d->mimeTypeInfo->groupInfo(d->name)->supportsVariableKeys(); } bool KFileMetaInfoGroup::contains( const TQString& key ) const { return d->items.contains(key); } KFileMetaInfoItem KFileMetaInfoGroup::item( const TQString& key) const { TQMapIterator it = d->items.find( key ); if ( it != d->items.end() ) return it.data(); return KFileMetaInfoItem(); } KFileMetaInfoItem KFileMetaInfoGroup::item(uint hint) const { TQMapIterator it; for (it = d->items.begin(); it!=d->items.end(); ++it) if (it.data().hint() == hint) return it.data(); return KFileMetaInfoItem(); } TQString KFileMetaInfoGroup::name() const { return d->name; } uint KFileMetaInfoGroup::attributes() const { assert(isValid()); return d->mimeTypeInfo->groupInfo(d->name)->attributes(); } void KFileMetaInfoGroup::setAdded() { d->added = true; } bool KFileMetaInfoGroup::isModified() const { return d->dirty; } void KFileMetaInfoGroup::ref() { if (d != Data::null) d->ref(); } void KFileMetaInfoGroup::deref() { // We don't call makeNull here since it isn't necassery: // If d is equal to null it means that null is initialized already. // null is 0L when it hasn't been initialized and d is never 0L. if ((d != Data::null) && d->deref()) { // kdDebug(7033) << "metainfo group " << d->name // << " is finally deleted\n"; delete d; d = 0; } } KFileMetaInfoItem KFileMetaInfoGroup::addItem( const TQString& key ) { assert(isValid()); TQMapIterator it = d->items.find( key ); if ( it != d->items.end() ) return it.data(); const KFileMimeTypeInfo::GroupInfo* ginfo = d->mimeTypeInfo->groupInfo(d->name); if ( !ginfo ) { Q_ASSERT( ginfo ); return KFileMetaInfoItem(); } const KFileMimeTypeInfo::ItemInfo* info = ginfo->itemInfo(key); if ( !info ) { Q_ASSERT( info ); return KFileMetaInfoItem(); } KFileMetaInfoItem item; if (info->isVariableItem()) item = KFileMetaInfoItem(ginfo->variableItemInfo(), key, TQVariant()); else item = KFileMetaInfoItem(info, key, TQVariant()); d->items.insert(key, item); item.setAdded(); // mark as added d->dirty = true; // mark ourself as dirty, too return item; } bool KFileMetaInfoGroup::removeItem( const TQString& key ) { if (!isValid()) { kdDebug(7033) << "trying to remove an item from an invalid group\n"; return false; } TQMapIterator it = d->items.find(key); if ( it==d->items.end() ) { kdDebug(7033) << "trying to remove the non existant item " << key << "\n"; return false; } if (!((*it).attributes() & KFileMimeTypeInfo::Removable)) { kdDebug(7033) << "trying to remove a non removable item\n"; return false; } (*it).setRemoved(); d->items.remove(it); d->removedItems.append(key); d->dirty = true; return true; } TQStringList KFileMetaInfoGroup::removedItems() { return d->removedItems; } KFileMetaInfoItem KFileMetaInfoGroup::appendItem(const TQString& key, const TQVariant& value) { //KDE4 enforce (value.type() == d->mimeTypeInfo->type()) assert(isValid()); const KFileMimeTypeInfo::GroupInfo* ginfo = d->mimeTypeInfo->groupInfo(d->name); if ( !ginfo ) { kdWarning() << "Trying to append a Metadata item for a non-existant group:" << d->name << endl; return KFileMetaInfoItem(); } const KFileMimeTypeInfo::ItemInfo* info = ginfo->itemInfo(key); if ( !info ) { kdWarning() << "Trying to append a Metadata item for an unknown key (no ItemInfo): " << key << endl; return KFileMetaInfoItem(); } KFileMetaInfoItem item; if (info->key().isNull()) item = KFileMetaInfoItem(ginfo->variableItemInfo(), key, value); else item = KFileMetaInfoItem(info, key, value); kdDebug(7033) << "KFileMetaInfogroup inserting a " << key << endl; d->items.insert(key, item); return item; } KFileMetaInfoGroup::Data* KFileMetaInfoGroup::Data::null = 0L; static KStaticDeleter sd_KFileMetaInfoGroupData; KFileMetaInfoGroup::Data* KFileMetaInfoGroup::Data::makeNull() { if (!null) { // We deliberately do not reset "null" after it has been destroyed! // Otherwise we will run into problems later in ~KFileMetaInfoItem // where the d-pointer is compared against null. null = new Data(TQString::null); null->mimeTypeInfo = new KFileMimeTypeInfo(); sd_KFileMetaInfoGroupData.setObject( null ); } return null; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// KFileMimeTypeInfo::KFileMimeTypeInfo( const TQString& mimeType ) : m_mimeType( mimeType ) { m_groups.setAutoDelete( true ); } KFileMimeTypeInfo::~KFileMimeTypeInfo() { } const KFileMimeTypeInfo::GroupInfo * KFileMimeTypeInfo::groupInfo( const TQString& group ) const { return m_groups.find( group ); } KFileMimeTypeInfo::GroupInfo * KFileMimeTypeInfo::addGroupInfo( const TQString& name, const TQString& translatedName ) { GroupInfo* group = new GroupInfo( name, translatedName ); m_groups.insert(name, group); return group; } TQStringList KFileMimeTypeInfo::supportedGroups() const { TQStringList list; TQDictIterator it( m_groups ); for ( ; it.current(); ++it ) list.append( it.current()->name() ); return list; } TQStringList KFileMimeTypeInfo::translatedGroups() const { TQStringList list; TQDictIterator it( m_groups ); for ( ; it.current(); ++it ) list.append( it.current()->translatedName() ); return list; } TQStringList KFileMimeTypeInfo::supportedKeys() const { // not really efficient, but not those are not large lists, probably. // maybe cache the result? TQStringList keys; TQStringList::ConstIterator lit; TQDictIterator it( m_groups ); for ( ; it.current(); ++it ) { // need to nuke dupes TQStringList list = it.current()->supportedKeys(); for ( lit = list.begin(); lit != list.end(); ++lit ) { if ( keys.find( *lit ) == keys.end() ) keys.append( *lit ); } } return keys; } TQValidator * KFileMimeTypeInfo::createValidator(const TQString& group, const TQString& key, TQObject *parent, const char *name) const { KFilePlugin* plugin = KFileMetaInfoProvider::self()->plugin(m_mimeType); if (plugin) return plugin->createValidator(mimeType(), group, key, parent, name); return 0; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// KFileMimeTypeInfo::GroupInfo::GroupInfo( const TQString& name, const TQString& translatedName ) : m_name( name ), m_translatedName( translatedName ), m_attr( 0 ), m_variableItemInfo( 0 ) { m_itemDict.setAutoDelete( true ); } KFileMimeTypeInfo::GroupInfo::~GroupInfo() { delete m_variableItemInfo; } const KFileMimeTypeInfo::ItemInfo * KFileMimeTypeInfo::GroupInfo::itemInfo( const TQString& key ) const { ItemInfo* item = m_itemDict.find( key ); // if we the item isn't found and variable keys are supported, we need to // return the default variable key iteminfo. if (!item && m_variableItemInfo) { return m_variableItemInfo; } return item; } KFileMimeTypeInfo::ItemInfo* KFileMimeTypeInfo::GroupInfo::addItemInfo( const TQString& key, const TQString& translatedKey, TQVariant::Type type) { // kdDebug(7034) << key << "(" << translatedKey << ") -> " << TQVariant::typeToName(type) << endl; ItemInfo* item = new ItemInfo(key, translatedKey, type); m_supportedKeys.append(key); m_itemDict.insert(key, item); return item; } void KFileMimeTypeInfo::GroupInfo::addVariableInfo( TQVariant::Type type, uint attr ) { // just make sure that it's not already there delete m_variableItemInfo; m_variableItemInfo = new ItemInfo(TQString::null, TQString::null, type); m_variableItemInfo->m_attr = attr; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// TQString KFileMimeTypeInfo::ItemInfo::string(const TQVariant& value, bool mangle) const { TQString s; switch (value.type()) { case TQVariant::Invalid : return "---"; case TQVariant::Bool : s = value.toBool() ? i18n("Yes") : i18n("No"); break; case TQVariant::Int : if (unit() == KFileMimeTypeInfo::Seconds) { int seconds = value.toInt() % 60; int minutes = value.toInt() / 60 % 60; int hours = value.toInt() / 3600; s = hours ? TQString().sprintf("%d:%02d:%02d",hours, minutes, seconds) : TQString().sprintf("%02d:%02d", minutes, seconds); return s; // no suffix wanted } else if (unit() == KFileMimeTypeInfo::Bytes) { // convertSize already adds the correct suffix return TDEIO::convertSize(value.toInt()); } else if (unit() == KFileMimeTypeInfo::KiloBytes) { // convertSizeFromKB already adds the correct suffix return TDEIO::convertSizeFromKB(value.toInt()); } else s = TDEGlobal::locale()->formatNumber( value.toInt() , 0); break; case TQVariant::LongLong : s = TDEGlobal::locale()->formatNumber( value.toLongLong(), 0 ); break; case TQVariant::ULongLong : if ( unit() == KFileMimeTypeInfo::Bytes ) return TDEIO::convertSize( value.toULongLong() ); else if ( unit() == KFileMimeTypeInfo::KiloBytes ) return TDEIO::convertSizeFromKB( value.toULongLong() ); else s = TDEGlobal::locale()->formatNumber( value.toULongLong(), 0 ); break; case TQVariant::UInt : s = TDEGlobal::locale()->formatNumber( value.toUInt() , 0); break; case TQVariant::Double : s = TDEGlobal::locale()->formatNumber( value.toDouble(), 3); break; case TQVariant::Date : s = TDEGlobal::locale()->formatDate( value.toDate(), true ); break; case TQVariant::Time : s = TDEGlobal::locale()->formatTime( value.toTime(), true ); break; case TQVariant::DateTime : s = TDEGlobal::locale()->formatDateTime( value.toDateTime(), true, true ); break; case TQVariant::Size : s = TQString("%1 x %2").arg(value.toSize().width()) .arg(value.toSize().height()); break; case TQVariant::Point : s = TQString("%1/%2").arg(value.toSize().width()) .arg(value.toSize().height()); break; default: s = value.toString(); } if (mangle && !s.isNull()) { s.prepend(prefix()); s.append(" " + suffix()); } return s; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// // stream operators /* serialization of a KFileMetaInfoItem: first a bool that says if the items is valid, and if yes, all the elements of the Data */ TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoItem& item ) { KFileMetaInfoItem::Data* d = item.d; // if the object is invalid, put only a char in the stream bool isValid = item.isValid(); s << isValid; // ### what do about mimetypeInfo ? if (isValid) s << d->key << d->value << d->dirty << d->added << d->removed; return s; } TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoItem& item ) { bool isValid; s >> isValid; if (!isValid) { item = KFileMetaInfoItem(); return s; } // we need a new object for our data item.deref(); item.d = new KFileMetaInfoItem::Data(); // ### what do about mimetypeInfo ? bool dirty, added, removed; s >> item.d->key >> item.d->value >> dirty >> added >> removed; item.d->dirty = dirty; item.d->added = added; item.d->removed = removed; return s; } // serialization of a KFileMetaInfoGroup // we serialize the name of the mimetype here instead of the mimetype info // on the other side, we can simply use this to ask the provider for the info TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoGroup& group ) { KFileMetaInfoGroup::Data* d = group.d; // if the object is invalid, put only a byte in the stream bool isValid = group.isValid(); s << isValid; if (isValid) { s << d->name << d->items << d->mimeTypeInfo->mimeType(); } return s; } TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoGroup& group ) { TQString mimeType; bool isValid; s >> isValid; // if it's invalid, there is not much to do if (!isValid) { group = KFileMetaInfoGroup(); return s; } // we need a new object for our data group.deref(); group.d = new KFileMetaInfoGroup::Data(); s >> group.d->name >> group.d->items >> mimeType; group.d->mimeTypeInfo = KFileMetaInfoProvider::self()->mimeTypeInfo(mimeType); // we need to set the item info for the items here TQMapIterator it = group.d->items.begin(); for ( ; it != group.d->items.end(); ++it) { (*it).d->mimeTypeInfo = group.d->mimeTypeInfo->groupInfo(group.d->name) ->itemInfo((*it).key()); } return s; } // serialization of a KFileMetaInfo object // we serialize the name of the mimetype here instead of the mimetype info // on the other side, we can simply use this to ask the provider for the info TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfo& info ) { KFileMetaInfo::Data* d = info.d; // if the object is invalid, put only a byte that tells this bool isValid = info.isValid(); s << isValid; if (isValid) { s << d->url << d->what << d->groups << d->mimeTypeInfo->mimeType(); } return s; } TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfo& info ) { TQString mimeType; bool isValid; s >> isValid; // if it's invalid, there is not much to do if (!isValid) { info = KFileMetaInfo(); return s; } // we need a new object for our data info.deref(); info.d = new KFileMetaInfo::Data(); s >> info.d->url >> info.d->what >> info.d->groups >> mimeType; info.d->mimeTypeInfo = KFileMetaInfoProvider::self()->mimeTypeInfo(mimeType); return s; } #include "tdefilemetainfo.moc"