/* This file is part of the KOffice libraries Copyright (C) 2001 Werner Trobin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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. */ #include #include #include #include #include #include // KoFilterManager::filterAvailable, private API #include #include #include #include // UINT_MAX // Those "defines" are needed in the setupConnections method below. // Please always keep the strings and the length in sync! namespace { const char* const SIGNAL_PREFIX = "commSignal"; const int SIGNAL_PREFIX_LEN = 10; const char* const SLOT_PREFIX = "commSlot"; const int SLOT_PREFIX_LEN = 8; } KoFilterChain::ChainLink::ChainLink( KoFilterChain* chain, KoFilterEntry::Ptr filterEntry, const TQCString& from, const TQCString& to ) : m_chain( chain ), m_filterEntry( filterEntry ), m_from( from ), m_to( to ), m_filter( 0 ), d( 0 ) { } KoFilter::ConversionStatus KoFilterChain::ChainLink::invokeFilter( const ChainLink* const parentChainLink ) { if ( !m_filterEntry ) { kdError( 30500 ) << "This filter entry is null. Strange stuff going on." << endl; return KoFilter::CreationError; } m_filter = m_filterEntry->createFilter( m_chain, 0, 0 ); if ( !m_filter ) { kdError( 30500 ) << "Couldn't create the filter." << endl; return KoFilter::CreationError; } if ( parentChainLink ) setupCommunication( parentChainLink->m_filter ); KoFilter::ConversionStatus status = m_filter->convert( m_from, m_to ); delete m_filter; m_filter=0; return status; } void KoFilterChain::ChainLink::dump() const { kdDebug( 30500 ) << " Link: " << m_filterEntry->service()->name() << endl; } int KoFilterChain::ChainLink::lruPartIndex() const { if ( m_filter && m_filter->inherits( "KoEmbeddingFilter" ) ) return static_cast( m_filter )->lruPartIndex(); return -1; } void KoFilterChain::ChainLink::setupCommunication( const KoFilter* const parentFilter ) const { // progress information TQObject::connect( m_filter, TQT_SIGNAL( sigProgress( int ) ), m_chain->manager(), TQT_SIGNAL( sigProgress( int ) ) ); if ( !parentFilter ) return; const TQMetaObject* const parent = parentFilter->metaObject(); const TQMetaObject* const child = m_filter->metaObject(); if ( !parent || !child ) return; setupConnections( parentFilter, parent->signalNames(), m_filter, child->slotNames() ); setupConnections( m_filter, child->signalNames(), parentFilter, parent->slotNames() ); } void KoFilterChain::ChainLink::setupConnections( const KoFilter* sender, const TQStrList& sigs, const KoFilter* receiver, const TQStrList& sl0ts ) const { TQStrListIterator signalIt( sigs ); for ( ; signalIt.current(); ++signalIt ) { if ( strncmp( signalIt.current(), SIGNAL_PREFIX, SIGNAL_PREFIX_LEN ) == 0 ) { TQStrListIterator slotIt( sl0ts ); for ( ; slotIt.current(); ++slotIt ) { if ( strncmp( slotIt.current(), SLOT_PREFIX, SLOT_PREFIX_LEN ) == 0 ) { if ( strcmp( signalIt.current() + SIGNAL_PREFIX_LEN, slotIt.current() + SLOT_PREFIX_LEN ) == 0 ) { TQCString signalString; signalString.setNum( TQSIGNAL_CODE ); signalString += signalIt.current(); TQCString slotString; slotString.setNum( TQSLOT_CODE ); slotString += slotIt.current(); TQObject::connect( sender, signalString, receiver, slotString ); } } } } } } KoFilterChain::~KoFilterChain() { if ( filterManagerParentChain() && filterManagerParentChain()->m_outputStorage ) filterManagerParentChain()->m_outputStorage->leaveDirectory(); manageIO(); // Called for the 2nd time in a row -> clean up } KoFilter::ConversionStatus KoFilterChain::invokeChain() { KoFilter::ConversionStatus status = KoFilter::OK; m_state = Beginning; int count = m_chainLinks.count(); // This is needed due to nasty Microsoft design const ChainLink* parentChainLink = 0; if ( filterManagerParentChain() ) parentChainLink = filterManagerParentChain()->m_chainLinks.current(); // No iterator here, as we need m_chainLinks.current() in outputDocument() m_chainLinks.first(); for ( ; count > 1 && m_chainLinks.current() && status == KoFilter::OK; m_chainLinks.next(), --count ) { status = m_chainLinks.current()->invokeFilter( parentChainLink ); m_state = Middle; manageIO(); } if ( !m_chainLinks.current() ) { kdWarning( 30500 ) << "Huh?? Found a null pointer in the chain" << endl; return KoFilter::StupidError; } if ( status == KoFilter::OK ) { if ( m_state & Beginning ) m_state |= End; else m_state = End; status = m_chainLinks.current()->invokeFilter( parentChainLink ); manageIO(); } m_state = Done; if (status == KoFilter::OK) finalizeIO(); return status; } TQString KoFilterChain::chainOutput() const { if ( m_state == Done ) return m_inputFile; // as we already called manageIO() return TQString(); } TQString KoFilterChain::inputFile() { if ( m_inputQueried == File ) return m_inputFile; else if ( m_inputQueried != Nil ) { kdWarning( 30500 ) << "You already asked for some different source." << endl; return TQString(); } m_inputQueried = File; if ( m_state & Beginning ) { if ( static_cast( filterManagerDirection() ) == KoFilterManager::Import ) m_inputFile = filterManagerImportFile(); else inputFileHelper( filterManagerKoDocument(), filterManagerImportFile() ); } else if ( m_inputFile.isEmpty() ) inputFileHelper( m_inputDocument, TQString() ); return m_inputFile; } TQString KoFilterChain::outputFile() { // sanity check: No embedded filter should ask for a plain file // ###### CHECK: This will break as soon as we support exporting embedding filters if ( filterManagerParentChain() ) kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl; if ( m_outputQueried == File ) return m_outputFile; else if ( m_outputQueried != Nil ) { kdWarning( 30500 ) << "You already asked for some different destination." << endl; return TQString(); } m_outputQueried = File; if ( m_state & End ) { if ( static_cast( filterManagerDirection() ) == KoFilterManager::Import ) outputFileHelper( false ); // This (last) one gets deleted by the caller else m_outputFile = filterManagerExportFile(); } else outputFileHelper( true ); return m_outputFile; } KoStoreDevice* KoFilterChain::storageFile( const TQString& name, KoStore::Mode mode ) { // ###### CHECK: This works only for import filters. Do we want something like // that for export filters too? if ( m_outputQueried == Nil && mode == KoStore::Write && filterManagerParentChain() ) return storageInitEmbedding( name ); // Plain normal use case if ( m_inputQueried == Storage && mode == KoStore::Read && m_inputStorage && m_inputStorage->mode() == KoStore::Read ) return storageNewStreamHelper( &m_inputStorage, &m_inputStorageDevice, name ); else if ( m_outputQueried == Storage && mode == KoStore::Write && m_outputStorage && m_outputStorage->mode() == KoStore::Write ) return storageNewStreamHelper( &m_outputStorage, &m_outputStorageDevice, name ); else if ( m_inputQueried == Nil && mode == KoStore::Read ) return storageHelper( inputFile(), name, KoStore::Read, &m_inputStorage, &m_inputStorageDevice ); else if ( m_outputQueried == Nil && mode == KoStore::Write ) return storageHelper( outputFile(), name, KoStore::Write, &m_outputStorage, &m_outputStorageDevice ); else { kdWarning( 30500 ) << "Oooops, how did we get here? You already asked for a" << " different source/destination?" << endl; return 0; } } KoDocument* KoFilterChain::inputDocument() { if ( m_inputQueried == Document ) return m_inputDocument; else if ( m_inputQueried != Nil ) { kdWarning( 30500 ) << "You already asked for some different source." << endl; return 0; } if ( ( m_state & Beginning ) && static_cast( filterManagerDirection() ) == KoFilterManager::Export && filterManagerKoDocument() ) m_inputDocument = filterManagerKoDocument(); else if ( !m_inputDocument ) m_inputDocument = createDocument( inputFile() ); m_inputQueried = Document; return m_inputDocument; } KoDocument* KoFilterChain::outputDocument() { // sanity check: No embedded filter should ask for a document // ###### CHECK: This will break as soon as we support exporting embedding filters if ( filterManagerParentChain() ) { kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl; return 0; } if ( m_outputQueried == Document ) return m_outputDocument; else if ( m_outputQueried != Nil ) { kdWarning( 30500 ) << "You already asked for some different destination." << endl; return 0; } if ( ( m_state & End ) && static_cast( filterManagerDirection() ) == KoFilterManager::Import && filterManagerKoDocument() ) m_outputDocument = filterManagerKoDocument(); else m_outputDocument = createDocument( m_chainLinks.current()->to() ); m_outputQueried = Document; return m_outputDocument; } void KoFilterChain::dump() const { kdDebug( 30500 ) << "########## KoFilterChain with " << m_chainLinks.count() << " members:" << endl; TQPtrListIterator it( m_chainLinks ); for ( ; it.current(); ++it ) it.current()->dump(); kdDebug( 30500 ) << "########## KoFilterChain (done) ##########" << endl; } KoFilterChain::KoFilterChain( const KoFilterManager* manager ) : m_manager( manager ), m_state( Beginning ), m_inputStorage( 0 ), m_inputStorageDevice( 0 ), m_outputStorage( 0 ), m_outputStorageDevice( 0 ), m_inputDocument( 0 ), m_outputDocument( 0 ), m_inputTempFile( 0 ), m_outputTempFile( 0 ), m_inputQueried( Nil ), m_outputQueried( Nil ), d( 0 ) { // We "own" our chain links, the filter entries are implicitly shared m_chainLinks.setAutoDelete( true ); } void KoFilterChain::appendChainLink( KoFilterEntry::Ptr filterEntry, const TQCString& from, const TQCString& to ) { m_chainLinks.append( new ChainLink( this, filterEntry, from, to ) ); } void KoFilterChain::prependChainLink( KoFilterEntry::Ptr filterEntry, const TQCString& from, const TQCString& to ) { m_chainLinks.prepend( new ChainLink( this, filterEntry, from, to ) ); } void KoFilterChain::enterDirectory( const TQString& directory ) { // Only a little bit of checking as we (have to :} ) trust KoEmbeddingFilter // If the output storage isn't initialized yet, we perform that step(s) on init. if ( m_outputStorage ) m_outputStorage->enterDirectory( directory ); m_internalEmbeddingDirectories.append( directory ); } void KoFilterChain::leaveDirectory() { if ( m_outputStorage ) m_outputStorage->leaveDirectory(); if ( !m_internalEmbeddingDirectories.isEmpty() ) m_internalEmbeddingDirectories.pop_back(); } TQString KoFilterChain::filterManagerImportFile() const { return m_manager->importFile(); } TQString KoFilterChain::filterManagerExportFile() const { return m_manager->exportFile(); } KoDocument* KoFilterChain::filterManagerKoDocument() const { return m_manager->document(); } int KoFilterChain::filterManagerDirection() const { return m_manager->direction(); } KoFilterChain* const KoFilterChain::filterManagerParentChain() const { return m_manager->parentChain(); } void KoFilterChain::manageIO() { m_inputQueried = Nil; m_outputQueried = Nil; delete m_inputStorageDevice; m_inputStorageDevice = 0; if ( m_inputStorage ) { m_inputStorage->close(); delete m_inputStorage; m_inputStorage = 0; } if ( m_inputTempFile ) { m_inputTempFile->close(); delete m_inputTempFile; // autodelete m_inputTempFile = 0; } m_inputFile = TQString(); if ( !m_outputFile.isEmpty() ) { m_inputFile = m_outputFile; m_outputFile = TQString(); m_inputTempFile = m_outputTempFile; m_outputTempFile = 0; delete m_outputStorageDevice; m_outputStorageDevice = 0; if ( m_outputStorage ) { m_outputStorage->close(); // Don't delete the storage if we're just pointing to the // storage of the parent filter chain if ( !filterManagerParentChain() || m_outputStorage->mode() != KoStore::Write ) delete m_outputStorage; m_outputStorage = 0; } } if ( m_inputDocument != filterManagerKoDocument() ) delete m_inputDocument; m_inputDocument = m_outputDocument; m_outputDocument = 0; } void KoFilterChain::finalizeIO() { // In case we export (to a file, of course) and the last // filter chose to output a KoDocument we have to save it. // Should be very rare, but well... // Note: m_*input*Document as we already called manageIO() if ( m_inputDocument && static_cast( filterManagerDirection() ) == KoFilterManager::Export ) { kdDebug( 30500 ) << "Saving the output document to the export file" << endl; m_inputDocument->saveNativeFormat( filterManagerExportFile() ); m_inputFile = filterManagerExportFile(); } } bool KoFilterChain::createTempFile( KTempFile** tempFile, bool autoDelete ) { if ( *tempFile ) { kdError( 30500 ) << "Ooops, why is there already a temp file???" << endl; return false; } *tempFile = new KTempFile(); ( *tempFile )->setAutoDelete( autoDelete ); return ( *tempFile )->status() == 0; } void KoFilterChain::inputFileHelper( KoDocument* document, const TQString& alternativeFile ) { if ( document ) { if ( !createTempFile( &m_inputTempFile ) ) { delete m_inputTempFile; m_inputTempFile = 0; m_inputFile = TQString(); return; } if ( !document->saveNativeFormat( m_inputTempFile->name() ) ) { delete m_inputTempFile; m_inputTempFile = 0; m_inputFile = TQString(); return; } m_inputFile = m_inputTempFile->name(); } else m_inputFile = alternativeFile; } void KoFilterChain::outputFileHelper( bool autoDelete ) { if ( !createTempFile( &m_outputTempFile, autoDelete ) ) { delete m_outputTempFile; m_outputTempFile = 0; m_outputFile = TQString(); } else m_outputFile = m_outputTempFile->name(); } KoStoreDevice* KoFilterChain::storageNewStreamHelper( KoStore** storage, KoStoreDevice** device, const TQString& name ) { delete *device; *device = 0; if ( ( *storage )->isOpen() ) ( *storage )->close(); if ( ( *storage )->bad() ) return storageCleanupHelper( storage ); if ( !( *storage )->open( name ) ) return 0; *device = new KoStoreDevice( *storage ); return *device; } KoStoreDevice* KoFilterChain::storageHelper( const TQString& file, const TQString& streamName, KoStore::Mode mode, KoStore** storage, KoStoreDevice** device ) { if ( file.isEmpty() ) return 0; if ( *storage ) { kdDebug( 30500 ) << "Uh-oh, we forgot to clean up..." << endl; return 0; } storageInit( file, mode, storage ); if ( ( *storage )->bad() ) return storageCleanupHelper( storage ); // Seems that we got a valid storage, at least. Even if we can't open // the stream the "user" asked us to open, we nontheless change the // IOState from File to Storage, as it might be possible to open other streams if ( mode == KoStore::Read ) m_inputQueried = Storage; else // KoStore::Write m_outputQueried = Storage; return storageCreateFirstStream( streamName, storage, device ); } void KoFilterChain::storageInit( const TQString& file, KoStore::Mode mode, KoStore** storage ) { TQCString appIdentification( "" ); if ( mode == KoStore::Write ) { // To create valid storages we also have to add the mimetype // magic "applicationIndentifier" to the storage. // As only filters with a KOffice destination should query // for a storage to write to, we don't check the content of // the mimetype here. It doesn't do a lot of harm if someome // "abuses" this method. appIdentification = m_chainLinks.current()->to(); } *storage = KoStore::createStore( file, mode, appIdentification ); } KoStoreDevice* KoFilterChain::storageInitEmbedding( const TQString& name ) { if ( m_outputStorage ) { kdWarning( 30500 ) << "Ooops! Something's really screwed here." << endl; return 0; } m_outputStorage = filterManagerParentChain()->m_outputStorage; if ( !m_outputStorage ) { // If the storage of the parent hasn't been initialized yet, // we have to do that here. Quite nasty... storageInit( filterManagerParentChain()->outputFile(), KoStore::Write, &m_outputStorage ); // transfer the ownership filterManagerParentChain()->m_outputStorage = m_outputStorage; filterManagerParentChain()->m_outputQueried = Storage; } if ( m_outputStorage->isOpen() ) m_outputStorage->close(); // to be on the safe side, should never happen if ( m_outputStorage->bad() ) return storageCleanupHelper( &m_outputStorage ); m_outputQueried = Storage; // Now that we have a storage we have to change the directory // and remember it for later! const int lruPartIndex = filterManagerParentChain()->m_chainLinks.current()->lruPartIndex(); if ( lruPartIndex == -1 ) { kdError( 30500 ) << "Huh! You want to use embedding features w/o inheriting KoEmbeddingFilter?" << endl; return storageCleanupHelper( &m_outputStorage ); } if ( !m_outputStorage->enterDirectory( TQString( "part%1" ).arg( lruPartIndex ) ) ) return storageCleanupHelper( &m_outputStorage ); return storageCreateFirstStream( name, &m_outputStorage, &m_outputStorageDevice ); } KoStoreDevice* KoFilterChain::storageCreateFirstStream( const TQString& streamName, KoStore** storage, KoStoreDevice** device ) { // Before we go and create the first stream in this storage we // have to perform a little hack in case we're used by any ole-style // filter which utilizes internal embedding. Ugly, but well... if ( !m_internalEmbeddingDirectories.isEmpty() ) { TQStringList::ConstIterator it = m_internalEmbeddingDirectories.begin(); TQStringList::ConstIterator end = m_internalEmbeddingDirectories.end(); for ( ; it != end && ( *storage )->enterDirectory( *it ); ++it ); } if ( !( *storage )->open( streamName ) ) return 0; if ( *device ) { kdDebug( 30500 ) << "Uh-oh, we forgot to clean up the storage device!" << endl; ( *storage )->close(); return storageCleanupHelper( storage ); } *device = new KoStoreDevice( *storage ); return *device; } KoStoreDevice* KoFilterChain::storageCleanupHelper( KoStore** storage ) { // Take care not to delete the storage of the parent chain if ( *storage != m_outputStorage || !filterManagerParentChain() || ( *storage )->mode() != KoStore::Write ) delete *storage; *storage = 0; return 0; } KoDocument* KoFilterChain::createDocument( const TQString& file ) { KURL url; url.setPath( file ); KMimeType::Ptr t = KMimeType::findByURL( url, 0, true ); if ( t->name() == KMimeType::defaultMimeType() ) { kdError( 30500 ) << "No mimetype found for " << file << endl; return 0; } KoDocument *doc = createDocument( TQCString( t->name().latin1() ) ); if ( !doc || !doc->loadNativeFormat( file ) ) { kdError( 30500 ) << "Couldn't load from the file" << endl; delete doc; return 0; } return doc; } KoDocument* KoFilterChain::createDocument( const TQCString& mimeType ) { KoDocumentEntry entry = KoDocumentEntry::queryByMimeType(mimeType); if (entry.isEmpty()) { kdError( 30500 ) << "Couldn't find a part that can handle mimetype " << mimeType << endl; } KoDocument* doc = entry.createDoc(); /*entries.first().createDoc();*/ if ( !doc ) { kdError( 30500 ) << "Couldn't create the document" << endl; return 0; } return doc; } namespace KOffice { Edge::Edge( Vertex* vertex, KoFilterEntry::Ptr filterEntry ) : m_vertex( vertex ), m_filterEntry( filterEntry ), d( 0 ) { } void Edge::relax( const Vertex* predecessor, PriorityQueue& queue ) { if ( !m_vertex || !predecessor || !m_filterEntry ) return; if ( m_vertex->setKey( predecessor->key() + m_filterEntry->weight ) ) { queue.keyDecreased( m_vertex ); // maintain the heap property m_vertex->setPredecessor( predecessor ); } } void Edge::dump( const TQCString& indent ) const { if ( m_vertex ) kdDebug( 30500 ) << indent << "Edge -> '" << m_vertex->mimeType() << "' (" << m_filterEntry->weight << ")" << endl; else kdDebug( 30500 ) << indent << "Edge -> '(null)' (" << m_filterEntry->weight << ")" << endl; } Vertex::Vertex( const TQCString& mimeType ) : m_predecessor( 0 ), m_mimeType( mimeType ), m_weight( UINT_MAX ), m_index( -1 ), d( 0 ) { m_edges.setAutoDelete( true ); // we take ownership of added edges } bool Vertex::setKey( unsigned int key ) { if ( m_weight > key ) { m_weight = key; return true; } return false; } void Vertex::reset() { m_weight = UINT_MAX; m_predecessor = 0; } void Vertex::addEdge( const Edge* edge ) { if ( !edge || edge->weight() == 0 ) return; m_edges.append( edge ); } const Edge* Vertex::findEdge( const Vertex* vertex ) const { if ( !vertex ) return 0; const Edge* edge = 0; TQPtrListIterator it( m_edges ); for ( ; it.current(); ++it ) { if ( it.current()->vertex() == vertex && ( !edge || it.current()->weight() < edge->weight() ) ) edge = it.current(); } return edge; } void Vertex::relaxVertices( PriorityQueue& queue ) { for ( Edge *e = m_edges.first(); e; e = m_edges.next() ) e->relax( this, queue ); } void Vertex::dump( const TQCString& indent ) const { kdDebug( 30500 ) << indent << "Vertex: " << m_mimeType << " (" << m_weight << "):" << endl; const TQCString i( indent + " " ); TQPtrListIterator it( m_edges ); for ( ; it.current(); ++it ) it.current()->dump( i ); } Graph::Graph( const TQCString& from ) : m_vertices( 47 ), m_from( from ), m_graphValid( false ), d( 0 ) { m_vertices.setAutoDelete( true ); buildGraph(); shortestPaths(); // Will return after a single lookup if "from" is invalid (->no check here) } void Graph::setSourceMimeType( const TQCString& from ) { if ( from == m_from ) return; m_from = from; m_graphValid = false; // Initialize with "infinity" ... TQAsciiDictIterator it( m_vertices ); for ( ; it.current(); ++it ) it.current()->reset(); // ...and re-run the shortest path search for the new source mime shortestPaths(); } KoFilterChain::Ptr Graph::chain( const KoFilterManager* manager, TQCString& to ) const { if ( !isValid() || !manager ) return 0; if ( to.isEmpty() ) { // if the destination is empty we search the closest KOffice part to = findKOfficePart(); if ( to.isEmpty() ) // still empty? strange stuff... return 0; } const Vertex* vertex = m_vertices[ to ]; if ( !vertex || vertex->key() == UINT_MAX ) return 0; KoFilterChain::Ptr ret = new KoFilterChain( manager ); // Fill the filter chain with all filters on the path const Vertex* tmp = vertex->predecessor(); while ( tmp ) { const Edge* const edge = tmp->findEdge( vertex ); Q_ASSERT( edge ); ret->prependChainLink( edge->filterEntry(), tmp->mimeType(), vertex->mimeType() ); vertex = tmp; tmp = tmp->predecessor(); } return ret; } void Graph::dump() const { kdDebug( 30500 ) << "+++++++++ Graph::dump +++++++++" << endl; kdDebug( 30500 ) << "From: " << m_from << endl; TQAsciiDictIterator it( m_vertices ); for ( ; it.current(); ++it ) it.current()->dump( " " ); kdDebug( 30500 ) << "+++++++++ Graph::dump (done) +++++++++" << endl; } // Query the trader and create the vertices and edges representing // available mime types and filters. void Graph::buildGraph() { // Make sure that all available parts are added to the graph TQValueList parts( KoDocumentEntry::query() ); TQValueList::ConstIterator partIt( parts.begin() ); TQValueList::ConstIterator partEnd( parts.end() ); while ( partIt != partEnd ) { TQStringList nativeMimeTypes = ( *partIt ).service()->property( "X-TDE-ExtraNativeMimeTypes" ).toStringList(); nativeMimeTypes += ( *partIt ).service()->property( "X-TDE-NativeMimeType" ).toString(); TQStringList::ConstIterator it = nativeMimeTypes.begin(); TQStringList::ConstIterator end = nativeMimeTypes.end(); for ( ; it != end; ++it ) if ( !(*it).isEmpty() ) m_vertices.insert( (*it).latin1(), new Vertex( (*it).latin1() ) ); ++partIt; } TQValueList filters( KoFilterEntry::query() ); // no constraint here - we want *all* :) TQValueList::ConstIterator it = filters.begin(); TQValueList::ConstIterator end = filters.end(); for ( ; it != end; ++it ) { // First add the "starting points" to the dict TQStringList::ConstIterator importIt = ( *it )->import.begin(); TQStringList::ConstIterator importEnd = ( *it )->import.end(); for ( ; importIt != importEnd; ++importIt ) { const TQCString key = ( *importIt ).latin1(); // latin1 is okay here (werner) // already there? if ( !m_vertices[ key ] ) m_vertices.insert( key, new Vertex( key ) ); } // Are we allowed to use this filter at all? if ( KoFilterManager::filterAvailable( *it ) ) { TQStringList::ConstIterator exportIt = ( *it )->export_.begin(); TQStringList::ConstIterator exportEnd = ( *it )->export_.end(); for ( ; exportIt != exportEnd; ++exportIt ) { // First make sure the export vertex is in place const TQCString key = ( *exportIt ).latin1(); // latin1 is okay here Vertex* exp = m_vertices[ key ]; if ( !exp ) { exp = new Vertex( key ); m_vertices.insert( key, exp ); } // Then create the appropriate edges importIt = ( *it )->import.begin(); for ( ; importIt != importEnd; ++importIt ) m_vertices[ ( *importIt ).latin1() ]->addEdge( new Edge( exp, *it ) ); } } else kdDebug( 30500 ) << "Filter: " << ( *it )->service()->name() << " doesn't apply." << endl; } } // As all edges (=filters) are required to have a positive weight // we can use Dijkstra's shortest path algorithm from Cormen's // "Introduction to Algorithms" (p. 527) // Note: I did some adaptions as our data structures are slightly // different from the ones used in the book. Further we simply stop // the algorithm is we don't find any node with a weight != Infinity // (==UINT_MAX), as this means that the remaining nodes in the queue // aren't connected anyway. void Graph::shortestPaths() { // Is the requested start mime type valid? Vertex* from = m_vertices[ m_from ]; if ( !from ) return; // Inititalize start vertex from->setKey( 0 ); // Fill the priority queue with all the vertices PriorityQueue queue( m_vertices ); while ( !queue.isEmpty() ) { Vertex *min = queue.extractMinimum(); // Did we already relax all connected vertices? if ( min->key() == UINT_MAX ) break; min->relaxVertices( queue ); } m_graphValid = true; } TQCString Graph::findKOfficePart() const { // Here we simply try to find the closest KOffice mimetype TQValueList parts( KoDocumentEntry::query() ); TQValueList::ConstIterator partIt( parts.begin() ); TQValueList::ConstIterator partEnd( parts.end() ); const Vertex *v = 0; // Be sure that v gets initialized correctly while ( !v && partIt != partEnd ) { TQStringList nativeMimeTypes = ( *partIt ).service()->property( "X-TDE-ExtraNativeMimeTypes" ).toStringList(); nativeMimeTypes += ( *partIt ).service()->property( "X-TDE-NativeMimeType" ).toString(); TQStringList::ConstIterator it = nativeMimeTypes.begin(); TQStringList::ConstIterator end = nativeMimeTypes.end(); for ( ; !v && it != end; ++it ) if ( !(*it).isEmpty() ) v = m_vertices[ ( *it ).latin1() ]; ++partIt; } if ( !v ) return ""; // Now we try to find the "cheapest" KOffice vertex while ( partIt != partEnd ) { TQStringList nativeMimeTypes = ( *partIt ).service()->property( "X-TDE-ExtraNativeMimeTypes" ).toStringList(); nativeMimeTypes += ( *partIt ).service()->property( "X-TDE-NativeMimeType" ).toString(); TQStringList::ConstIterator it = nativeMimeTypes.begin(); TQStringList::ConstIterator end = nativeMimeTypes.end(); for ( ; !v && it != end; ++it ) { TQString key = *it; if ( !key.isEmpty() ) { Vertex* tmp = m_vertices[ key.latin1() ]; if ( !v || ( tmp && tmp->key() < v->key() ) ) v = tmp; } } ++partIt; } // It seems it already is a KOffice part if ( v->key() == 0 ) return ""; return v->mimeType(); } } // namespace KOffice