lib Library API Documentation

koFilterChain.cpp

00001 /* This file is part of the KOffice libraries 00002 Copyright (C) 2001 Werner Trobin <trobin@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00016 Boston, MA 02111-1307, USA. 00017 */ 00018 00019 #include <qmetaobject.h> 00020 #include <ktempfile.h> 00021 #include <kmimetype.h> 00022 #include <koFilterChain.h> 00023 #include <koQueryTrader.h> 00024 #include <koFilterManager.h> // KoFilterManager::filterAvailable, private API 00025 #include <koDocument.h> 00026 #include <kdebug.h> 00027 00028 #include <priorityqueue.h> 00029 00030 #include <limits.h> // UINT_MAX 00031 00032 // Those "defines" are needed in the setupConnections method below. 00033 // Please always keep the strings and the length in sync! 00034 namespace { 00035 const char* const SIGNAL_PREFIX = "commSignal"; 00036 const int SIGNAL_PREFIX_LEN = 10; 00037 const char* const SLOT_PREFIX = "commSlot"; 00038 const int SLOT_PREFIX_LEN = 8; 00039 } 00040 00041 00042 KoFilterChain::ChainLink::ChainLink( KoFilterChain* chain, KoFilterEntry::Ptr filterEntry, 00043 const QCString& from, const QCString& to ) : 00044 m_chain( chain ), m_filterEntry( filterEntry ), m_from( from ), m_to( to ), 00045 m_filter( 0 ), d( 0 ) 00046 { 00047 } 00048 00049 KoFilter::ConversionStatus KoFilterChain::ChainLink::invokeFilter( const ChainLink* const parentChainLink ) 00050 { 00051 if ( !m_filterEntry ) { 00052 kdError( 30500 ) << "This filter entry is null. Strange stuff going on." << endl; 00053 return KoFilter::CreationError; 00054 } 00055 00056 m_filter = m_filterEntry->createFilter( m_chain, 0, 0 ); 00057 00058 if ( !m_filter ) { 00059 kdError( 30500 ) << "Couldn't create the filter." << endl; 00060 return KoFilter::CreationError; 00061 } 00062 00063 if ( parentChainLink ) 00064 setupCommunication( parentChainLink->m_filter ); 00065 00066 KoFilter::ConversionStatus status = m_filter->convert( m_from, m_to ); 00067 delete m_filter; 00068 m_filter=0; 00069 return status; 00070 } 00071 00072 void KoFilterChain::ChainLink::dump() const 00073 { 00074 kdDebug( 30500 ) << " Link: " << m_filterEntry->service()->name() << endl; 00075 } 00076 00077 int KoFilterChain::ChainLink::lruPartIndex() const 00078 { 00079 if ( m_filter && m_filter->inherits( "KoEmbeddingFilter" ) ) 00080 return static_cast<KoEmbeddingFilter*>( m_filter )->lruPartIndex(); 00081 return -1; 00082 } 00083 00084 void KoFilterChain::ChainLink::setupCommunication( const KoFilter* const parentFilter ) const 00085 { 00086 // progress information 00087 QObject::connect( m_filter, SIGNAL( sigProgress( int ) ), 00088 m_chain->manager(), SIGNAL( sigProgress( int ) ) ); 00089 00090 if ( !parentFilter ) 00091 return; 00092 00093 const QMetaObject* const parent = parentFilter->metaObject(); 00094 const QMetaObject* const child = m_filter->metaObject(); 00095 if ( !parent || !child ) 00096 return; 00097 00098 setupConnections( parentFilter, parent->signalNames(), m_filter, child->slotNames() ); 00099 setupConnections( m_filter, child->signalNames(), parentFilter, parent->slotNames() ); 00100 } 00101 00102 void KoFilterChain::ChainLink::setupConnections( const KoFilter* sender, const QStrList& sigs, 00103 const KoFilter* receiver, const QStrList& sl0ts ) const 00104 { 00105 QStrListIterator signalIt( sigs ); 00106 for ( ; signalIt.current(); ++signalIt ) { 00107 if ( strncmp( signalIt.current(), SIGNAL_PREFIX, SIGNAL_PREFIX_LEN ) == 0 ) { 00108 QStrListIterator slotIt( sl0ts ); 00109 for ( ; slotIt.current(); ++slotIt ) { 00110 if ( strncmp( slotIt.current(), SLOT_PREFIX, SLOT_PREFIX_LEN ) == 0 ) { 00111 if ( strcmp( signalIt.current() + SIGNAL_PREFIX_LEN, slotIt.current() + SLOT_PREFIX_LEN ) == 0 ) { 00112 QCString signalString; 00113 signalString.setNum( QSIGNAL_CODE ); 00114 signalString += signalIt.current(); 00115 QCString slotString; 00116 slotString.setNum( QSLOT_CODE ); 00117 slotString += slotIt.current(); 00118 QObject::connect( sender, signalString, receiver, slotString ); 00119 } 00120 } 00121 } 00122 } 00123 } 00124 } 00125 00126 00127 KoFilterChain::~KoFilterChain() 00128 { 00129 if ( filterManagerParentChain() && filterManagerParentChain()->m_outputStorage ) 00130 filterManagerParentChain()->m_outputStorage->leaveDirectory(); 00131 manageIO(); // Called for the 2nd time in a row -> clean up 00132 } 00133 00134 KoFilter::ConversionStatus KoFilterChain::invokeChain() 00135 { 00136 KoFilter::ConversionStatus status = KoFilter::OK; 00137 00138 m_state = Beginning; 00139 int count = m_chainLinks.count(); 00140 00141 // This is needed due to nasty Microsoft design 00142 const ChainLink* parentChainLink = 0; 00143 if ( filterManagerParentChain() ) 00144 parentChainLink = filterManagerParentChain()->m_chainLinks.current(); 00145 00146 // No iterator here, as we need m_chainLinks.current() in outputDocument() 00147 m_chainLinks.first(); 00148 for ( ; count > 1 && m_chainLinks.current() && status == KoFilter::OK; 00149 m_chainLinks.next(), --count ) { 00150 status = m_chainLinks.current()->invokeFilter( parentChainLink ); 00151 m_state = Middle; 00152 manageIO(); 00153 } 00154 00155 if ( !m_chainLinks.current() ) { 00156 kdWarning( 30500 ) << "Huh?? Found a null pointer in the chain" << endl; 00157 return KoFilter::StupidError; 00158 } 00159 00160 if ( status == KoFilter::OK ) { 00161 if ( m_state & Beginning ) 00162 m_state |= End; 00163 else 00164 m_state = End; 00165 status = m_chainLinks.current()->invokeFilter( parentChainLink ); 00166 manageIO(); 00167 } 00168 00169 m_state = Done; 00170 finalizeIO(); 00171 return status; 00172 } 00173 00174 QString KoFilterChain::chainOutput() const 00175 { 00176 if ( m_state == Done ) 00177 return m_inputFile; // as we already called manageIO() 00178 return QString::null; 00179 } 00180 00181 QString KoFilterChain::inputFile() 00182 { 00183 if ( m_inputQueried == File ) 00184 return m_inputFile; 00185 else if ( m_inputQueried != Nil ) { 00186 kdWarning( 30500 ) << "You already asked for some different source." << endl; 00187 return QString::null; 00188 } 00189 m_inputQueried = File; 00190 00191 if ( m_state & Beginning ) { 00192 if ( static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == 00193 KoFilterManager::Import ) 00194 m_inputFile = filterManagerImportFile(); 00195 else 00196 inputFileHelper( filterManagerKoDocument(), filterManagerImportFile() ); 00197 } 00198 else 00199 if ( m_inputFile.isEmpty() ) 00200 inputFileHelper( m_inputDocument, QString::null ); 00201 00202 return m_inputFile; 00203 } 00204 00205 QString KoFilterChain::outputFile() 00206 { 00207 // sanity check: No embedded filter should ask for a plain file 00208 // ###### CHECK: This will break as soon as we support exporting embedding filters 00209 if ( filterManagerParentChain() ) 00210 kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl; 00211 00212 if ( m_outputQueried == File ) 00213 return m_outputFile; 00214 else if ( m_outputQueried != Nil ) { 00215 kdWarning( 30500 ) << "You already asked for some different destination." << endl; 00216 return QString::null; 00217 } 00218 m_outputQueried = File; 00219 00220 if ( m_state & End ) { 00221 if ( static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == 00222 KoFilterManager::Import ) 00223 outputFileHelper( false ); // This (last) one gets deleted by the caller 00224 else 00225 m_outputFile = filterManagerExportFile(); 00226 } 00227 else 00228 outputFileHelper( true ); 00229 00230 return m_outputFile; 00231 } 00232 00233 KoStoreDevice* KoFilterChain::storageFile( const QString& name, KoStore::Mode mode ) 00234 { 00235 // ###### CHECK: This works only for import filters. Do we want something like 00236 // that for export filters too? 00237 if ( m_outputQueried == Nil && mode == KoStore::Write && filterManagerParentChain() ) 00238 return storageInitEmbedding( name ); 00239 00240 // Plain normal use case 00241 if ( m_inputQueried == Storage && mode == KoStore::Read && 00242 m_inputStorage && m_inputStorage->mode() == KoStore::Read ) 00243 return storageNewStreamHelper( &m_inputStorage, &m_inputStorageDevice, name ); 00244 else if ( m_outputQueried == Storage && mode == KoStore::Write && 00245 m_outputStorage && m_outputStorage->mode() == KoStore::Write ) 00246 return storageNewStreamHelper( &m_outputStorage, &m_outputStorageDevice, name ); 00247 else if ( m_inputQueried == Nil && mode == KoStore::Read ) 00248 return storageHelper( inputFile(), name, KoStore::Read, 00249 &m_inputStorage, &m_inputStorageDevice ); 00250 else if ( m_outputQueried == Nil && mode == KoStore::Write ) 00251 return storageHelper( outputFile(), name, KoStore::Write, 00252 &m_outputStorage, &m_outputStorageDevice ); 00253 else { 00254 kdWarning( 30500 ) << "Oooops, how did we get here? You already asked for a" 00255 << " different source/destination?" << endl; 00256 return 0; 00257 } 00258 } 00259 00260 KoDocument* KoFilterChain::inputDocument() 00261 { 00262 if ( m_inputQueried == Document ) 00263 return m_inputDocument; 00264 else if ( m_inputQueried != Nil ) { 00265 kdWarning( 30500 ) << "You already asked for some different source." << endl; 00266 return 0; 00267 } 00268 00269 if ( ( m_state & Beginning ) && 00270 static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Export && 00271 filterManagerKoDocument() ) 00272 m_inputDocument = filterManagerKoDocument(); 00273 else if ( !m_inputDocument ) 00274 m_inputDocument = createDocument( inputFile() ); 00275 00276 m_inputQueried = Document; 00277 return m_inputDocument; 00278 } 00279 00280 KoDocument* KoFilterChain::outputDocument() 00281 { 00282 // sanity check: No embedded filter should ask for a document 00283 // ###### CHECK: This will break as soon as we support exporting embedding filters 00284 if ( filterManagerParentChain() ) { 00285 kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl; 00286 return 0; 00287 } 00288 00289 if ( m_outputQueried == Document ) 00290 return m_outputDocument; 00291 else if ( m_outputQueried != Nil ) { 00292 kdWarning( 30500 ) << "You already asked for some different destination." << endl; 00293 return 0; 00294 } 00295 00296 if ( ( m_state & End ) && 00297 static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Import && 00298 filterManagerKoDocument() ) 00299 m_outputDocument = filterManagerKoDocument(); 00300 else 00301 m_outputDocument = createDocument( m_chainLinks.current()->to() ); 00302 00303 m_outputQueried = Document; 00304 return m_outputDocument; 00305 } 00306 00307 void KoFilterChain::dump() const 00308 { 00309 kdDebug( 30500 ) << "########## KoFilterChain with " << m_chainLinks.count() << " members:" << endl; 00310 QPtrListIterator<ChainLink> it( m_chainLinks ); 00311 for ( ; it.current(); ++it ) 00312 it.current()->dump(); 00313 kdDebug( 30500 ) << "########## KoFilterChain (done) ##########" << endl; 00314 } 00315 00316 KoFilterChain::KoFilterChain( const KoFilterManager* manager ) : 00317 m_manager( manager ), m_state( Beginning ), m_inputStorage( 0 ), 00318 m_inputStorageDevice( 0 ), m_outputStorage( 0 ), m_outputStorageDevice( 0 ), 00319 m_inputDocument( 0 ), m_outputDocument( 0 ), m_inputTempFile( 0 ), 00320 m_outputTempFile( 0 ), m_inputQueried( Nil ), m_outputQueried( Nil ), d( 0 ) 00321 { 00322 // We "own" our chain links, the filter entries are implicitly shared 00323 m_chainLinks.setAutoDelete( true ); 00324 } 00325 00326 void KoFilterChain::appendChainLink( KoFilterEntry::Ptr filterEntry, const QCString& from, const QCString& to ) 00327 { 00328 m_chainLinks.append( new ChainLink( this, filterEntry, from, to ) ); 00329 } 00330 00331 void KoFilterChain::prependChainLink( KoFilterEntry::Ptr filterEntry, const QCString& from, const QCString& to ) 00332 { 00333 m_chainLinks.prepend( new ChainLink( this, filterEntry, from, to ) ); 00334 } 00335 00336 void KoFilterChain::enterDirectory( const QString& directory ) 00337 { 00338 // Only a little bit of checking as we (have to :} ) trust KoEmbeddingFilter 00339 // If the output storage isn't initialized yet, we perform that step(s) on init. 00340 if ( m_outputStorage ) 00341 m_outputStorage->enterDirectory( directory ); 00342 m_internalEmbeddingDirectories.append( directory ); 00343 } 00344 00345 void KoFilterChain::leaveDirectory() 00346 { 00347 if ( m_outputStorage ) 00348 m_outputStorage->leaveDirectory(); 00349 if ( !m_internalEmbeddingDirectories.isEmpty() ) 00350 m_internalEmbeddingDirectories.pop_back(); 00351 } 00352 00353 QString KoFilterChain::filterManagerImportFile() const 00354 { 00355 return m_manager->importFile(); 00356 } 00357 00358 QString KoFilterChain::filterManagerExportFile() const 00359 { 00360 return m_manager->exportFile(); 00361 } 00362 00363 KoDocument* KoFilterChain::filterManagerKoDocument() const 00364 { 00365 return m_manager->document(); 00366 } 00367 00368 int KoFilterChain::filterManagerDirection() const 00369 { 00370 return m_manager->direction(); 00371 } 00372 00373 KoFilterChain* const KoFilterChain::filterManagerParentChain() const 00374 { 00375 return m_manager->parentChain(); 00376 } 00377 00378 void KoFilterChain::manageIO() 00379 { 00380 m_inputQueried = Nil; 00381 m_outputQueried = Nil; 00382 00383 delete m_inputStorageDevice; 00384 m_inputStorageDevice = 0; 00385 if ( m_inputStorage ) { 00386 m_inputStorage->close(); 00387 delete m_inputStorage; 00388 m_inputStorage = 0; 00389 } 00390 if ( m_inputTempFile ) { 00391 m_inputTempFile->close(); 00392 delete m_inputTempFile; // autodelete 00393 m_inputTempFile = 0; 00394 } 00395 m_inputFile = QString::null; 00396 00397 if ( !m_outputFile.isEmpty() ) { 00398 m_inputFile = m_outputFile; 00399 m_outputFile = QString::null; 00400 m_inputTempFile = m_outputTempFile; 00401 m_outputTempFile = 0; 00402 00403 delete m_outputStorageDevice; 00404 m_outputStorageDevice = 0; 00405 if ( m_outputStorage ) { 00406 m_outputStorage->close(); 00407 // Don't delete the storage if we're just pointing to the 00408 // storage of the parent filter chain 00409 if ( !filterManagerParentChain() || m_outputStorage->mode() != KoStore::Write ) 00410 delete m_outputStorage; 00411 m_outputStorage = 0; 00412 } 00413 } 00414 00415 if ( m_inputDocument != filterManagerKoDocument() ) 00416 delete m_inputDocument; 00417 m_inputDocument = m_outputDocument; 00418 m_outputDocument = 0; 00419 } 00420 00421 void KoFilterChain::finalizeIO() 00422 { 00423 // In case we export (to a file, of course) and the last 00424 // filter chose to output a KoDocument we have to save it. 00425 // Should be very rare, but well... 00426 // Note: m_*input*Document as we already called manageIO() 00427 if ( m_inputDocument && 00428 static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Export ) { 00429 kdDebug( 30500 ) << "Saving the output document to the export file" << endl; 00430 m_inputDocument->saveNativeFormat( filterManagerExportFile() ); 00431 m_inputFile = filterManagerExportFile(); 00432 } 00433 } 00434 00435 bool KoFilterChain::createTempFile( KTempFile** tempFile, bool autoDelete ) 00436 { 00437 if ( *tempFile ) { 00438 kdError( 30500 ) << "Ooops, why is there already a temp file???" << endl; 00439 return false; 00440 } 00441 *tempFile = new KTempFile(); 00442 ( *tempFile )->setAutoDelete( autoDelete ); 00443 return ( *tempFile )->status() == 0; 00444 } 00445 00446 void KoFilterChain::inputFileHelper( KoDocument* document, const QString& alternativeFile ) 00447 { 00448 if ( document ) { 00449 if ( !createTempFile( &m_inputTempFile ) ) { 00450 delete m_inputTempFile; 00451 m_inputTempFile = 0; 00452 m_inputFile = QString::null; 00453 return; 00454 } 00455 if ( !document->saveNativeFormat( m_inputTempFile->name() ) ) { 00456 delete m_inputTempFile; 00457 m_inputTempFile = 0; 00458 m_inputFile = QString::null; 00459 return; 00460 } 00461 m_inputFile = m_inputTempFile->name(); 00462 } 00463 else 00464 m_inputFile = alternativeFile; 00465 } 00466 00467 void KoFilterChain::outputFileHelper( bool autoDelete ) 00468 { 00469 if ( !createTempFile( &m_outputTempFile, autoDelete ) ) { 00470 delete m_outputTempFile; 00471 m_outputTempFile = 0; 00472 m_outputFile = QString::null; 00473 } 00474 else 00475 m_outputFile = m_outputTempFile->name(); 00476 } 00477 00478 KoStoreDevice* KoFilterChain::storageNewStreamHelper( KoStore** storage, KoStoreDevice** device, 00479 const QString& name ) 00480 { 00481 delete *device; 00482 *device = 0; 00483 if ( ( *storage )->isOpen() ) 00484 ( *storage )->close(); 00485 if ( ( *storage )->bad() ) 00486 return storageCleanupHelper( storage ); 00487 if ( !( *storage )->open( name ) ) 00488 return 0; 00489 00490 *device = new KoStoreDevice( *storage ); 00491 return *device; 00492 } 00493 00494 KoStoreDevice* KoFilterChain::storageHelper( const QString& file, const QString& streamName, 00495 KoStore::Mode mode, KoStore** storage, 00496 KoStoreDevice** device ) 00497 { 00498 if ( file.isEmpty() ) 00499 return 0; 00500 if ( *storage ) { 00501 kdDebug( 30500 ) << "Uh-oh, we forgot to clean up..." << endl; 00502 return 0; 00503 } 00504 00505 storageInit( file, mode, storage ); 00506 00507 if ( ( *storage )->bad() ) 00508 return storageCleanupHelper( storage ); 00509 00510 // Seems that we got a valid storage, at least. Even if we can't open 00511 // the stream the "user" asked us to open, we nontheless change the 00512 // IOState from File to Storage, as it might be possible to open other streams 00513 if ( mode == KoStore::Read ) 00514 m_inputQueried = Storage; 00515 else // KoStore::Write 00516 m_outputQueried = Storage; 00517 00518 return storageCreateFirstStream( streamName, storage, device ); 00519 } 00520 00521 void KoFilterChain::storageInit( const QString& file, KoStore::Mode mode, KoStore** storage ) 00522 { 00523 QCString appIdentification( "" ); 00524 if ( mode == KoStore::Write ) { 00525 // To create valid storages we also have to add the mimetype 00526 // magic "applicationIndentifier" to the storage. 00527 // As only filters with a KOffice destination should query 00528 // for a storage to write to, we don't check the content of 00529 // the mimetype here. It doesn't do a lot of harm if someome 00530 // "abuses" this method. 00531 appIdentification = m_chainLinks.current()->to(); 00532 } 00533 *storage = KoStore::createStore( file, mode, appIdentification ); 00534 } 00535 00536 KoStoreDevice* KoFilterChain::storageInitEmbedding( const QString& name ) 00537 { 00538 if ( m_outputStorage ) { 00539 kdWarning( 30500 ) << "Ooops! Something's really screwed here." << endl; 00540 return 0; 00541 } 00542 00543 m_outputStorage = filterManagerParentChain()->m_outputStorage; 00544 00545 if ( !m_outputStorage ) { 00546 // If the storage of the parent hasn't been initialized yet, 00547 // we have to do that here. Quite nasty... 00548 storageInit( filterManagerParentChain()->outputFile(), KoStore::Write, &m_outputStorage ); 00549 00550 // transfer the ownership 00551 filterManagerParentChain()->m_outputStorage = m_outputStorage; 00552 filterManagerParentChain()->m_outputQueried = Storage; 00553 } 00554 00555 if ( m_outputStorage->isOpen() ) 00556 m_outputStorage->close(); // to be on the safe side, should never happen 00557 if ( m_outputStorage->bad() ) 00558 return storageCleanupHelper( &m_outputStorage ); 00559 00560 m_outputQueried = Storage; 00561 00562 // Now that we have a storage we have to change the directory 00563 // and remember it for later! 00564 const int lruPartIndex = filterManagerParentChain()->m_chainLinks.current()->lruPartIndex(); 00565 if ( lruPartIndex == -1 ) { 00566 kdError( 30500 ) << "Huh! You want to use embedding features w/o inheriting KoEmbeddingFilter?" << endl; 00567 return storageCleanupHelper( &m_outputStorage ); 00568 } 00569 00570 if ( !m_outputStorage->enterDirectory( QString( "part%1" ).arg( lruPartIndex ) ) ) 00571 return storageCleanupHelper( &m_outputStorage ); 00572 00573 return storageCreateFirstStream( name, &m_outputStorage, &m_outputStorageDevice ); 00574 } 00575 00576 KoStoreDevice* KoFilterChain::storageCreateFirstStream( const QString& streamName, KoStore** storage, 00577 KoStoreDevice** device ) 00578 { 00579 // Before we go and create the first stream in this storage we 00580 // have to perform a little hack in case we're used by any ole-style 00581 // filter which utilizes internal embedding. Ugly, but well... 00582 if ( !m_internalEmbeddingDirectories.isEmpty() ) { 00583 QStringList::ConstIterator it = m_internalEmbeddingDirectories.begin(); 00584 QStringList::ConstIterator end = m_internalEmbeddingDirectories.end(); 00585 for ( ; it != end && ( *storage )->enterDirectory( *it ); ++it ); 00586 } 00587 00588 if ( !( *storage )->open( streamName ) ) 00589 return 0; 00590 00591 if ( *device ) { 00592 kdDebug( 30500 ) << "Uh-oh, we forgot to clean up the storage device!" << endl; 00593 ( *storage )->close(); 00594 return storageCleanupHelper( storage ); 00595 } 00596 *device = new KoStoreDevice( *storage ); 00597 return *device; 00598 } 00599 00600 KoStoreDevice* KoFilterChain::storageCleanupHelper( KoStore** storage ) 00601 { 00602 // Take care not to delete the storage of the parent chain 00603 if ( *storage != m_outputStorage || !filterManagerParentChain() || 00604 ( *storage )->mode() != KoStore::Write ) 00605 delete *storage; 00606 *storage = 0; 00607 return 0; 00608 } 00609 00610 KoDocument* KoFilterChain::createDocument( const QString& file ) 00611 { 00612 KURL url; 00613 url.setPath( file ); 00614 KMimeType::Ptr t = KMimeType::findByURL( url, 0, true ); 00615 if ( t->name() == KMimeType::defaultMimeType() ) { 00616 kdError( 30500 ) << "No mimetype found for " << file << endl; 00617 return 0; 00618 } 00619 00620 KoDocument *doc = createDocument( QCString( t->name().latin1() ) ); 00621 00622 if ( !doc || !doc->loadNativeFormat( file ) ) { 00623 kdError( 30500 ) << "Couldn't load from the file" << endl; 00624 delete doc; 00625 return 0; 00626 } 00627 return doc; 00628 } 00629 00630 KoDocument* KoFilterChain::createDocument( const QCString& mimeType ) 00631 { 00632 const QString constraint( QString::fromLatin1( "[X-KDE-NativeMimeType] == '%1'" ).arg( mimeType ) ); 00633 QValueList<KoDocumentEntry> entries = KoDocumentEntry::query( constraint ); 00634 if ( entries.isEmpty() ) { 00635 kdError( 30500 ) << "Couldn't find a KOffice document entry for " << mimeType << endl; 00636 return 0; 00637 } 00638 00639 if ( entries.count() != 1 ) 00640 kdWarning( 30500 ) << "Huh?? Two document entries for the same mimetype?" 00641 << " Will take the first one." << endl; 00642 00643 KoDocument* doc = entries.first().createDoc(); 00644 if ( !doc ) { 00645 kdError( 30500 ) << "Couldn't create the document" << endl; 00646 return 0; 00647 } 00648 return doc; 00649 } 00650 00651 00652 namespace KOffice { 00653 00654 Edge::Edge( Vertex* vertex, KoFilterEntry::Ptr filterEntry ) : 00655 m_vertex( vertex ), m_filterEntry( filterEntry ), d( 0 ) 00656 { 00657 } 00658 00659 void Edge::relax( const Vertex* predecessor, PriorityQueue<Vertex>& queue ) 00660 { 00661 if ( !m_vertex || !predecessor || !m_filterEntry ) 00662 return; 00663 if ( m_vertex->setKey( predecessor->key() + m_filterEntry->weight ) ) { 00664 queue.keyDecreased( m_vertex ); // maintain the heap property 00665 m_vertex->setPredecessor( predecessor ); 00666 } 00667 } 00668 00669 void Edge::dump( const QCString& indent ) const 00670 { 00671 if ( m_vertex ) 00672 kdDebug( 30500 ) << indent << "Edge -> '" << m_vertex->mimeType() 00673 << "' (" << m_filterEntry->weight << ")" << endl; 00674 else 00675 kdDebug( 30500 ) << indent << "Edge -> '(null)' (" 00676 << m_filterEntry->weight << ")" << endl; 00677 } 00678 00679 00680 Vertex::Vertex( const QCString& mimeType ) : m_predecessor( 0 ), m_mimeType( mimeType ), 00681 m_weight( UINT_MAX ), m_index( -1 ), d( 0 ) 00682 { 00683 m_edges.setAutoDelete( true ); // we take ownership of added edges 00684 } 00685 00686 bool Vertex::setKey( unsigned int key ) 00687 { 00688 if ( m_weight > key ) { 00689 m_weight = key; 00690 return true; 00691 } 00692 return false; 00693 } 00694 00695 void Vertex::reset() 00696 { 00697 m_weight = UINT_MAX; 00698 m_predecessor = 0; 00699 } 00700 00701 void Vertex::addEdge( const Edge* edge ) 00702 { 00703 if ( !edge || edge->weight() == 0 ) 00704 return; 00705 m_edges.append( edge ); 00706 } 00707 00708 const Edge* Vertex::findEdge( const Vertex* vertex ) const 00709 { 00710 if ( !vertex ) 00711 return 0; 00712 const Edge* edge = 0; 00713 QPtrListIterator<Edge> it( m_edges ); 00714 00715 for ( ; it.current(); ++it ) { 00716 if ( it.current()->vertex() == vertex && 00717 ( !edge || it.current()->weight() < edge->weight() ) ) 00718 edge = it.current(); 00719 } 00720 return edge; 00721 } 00722 00723 void Vertex::relaxVertices( PriorityQueue<Vertex>& queue ) 00724 { 00725 for ( Edge *e = m_edges.first(); e; e = m_edges.next() ) 00726 e->relax( this, queue ); 00727 } 00728 00729 void Vertex::dump( const QCString& indent ) const 00730 { 00731 kdDebug( 30500 ) << indent << "Vertex: " << m_mimeType << " (" << m_weight << "):" << endl; 00732 const QCString i( indent + " " ); 00733 QPtrListIterator<Edge> it( m_edges ); 00734 for ( ; it.current(); ++it ) 00735 it.current()->dump( i ); 00736 } 00737 00738 00739 Graph::Graph( const QCString& from ) : m_vertices( 47 ), m_from( from ), 00740 m_graphValid( false ), d( 0 ) 00741 { 00742 m_vertices.setAutoDelete( true ); 00743 buildGraph(); 00744 shortestPaths(); // Will return after a single lookup if "from" is invalid (->no check here) 00745 } 00746 00747 void Graph::setSourceMimeType( const QCString& from ) 00748 { 00749 if ( from == m_from ) 00750 return; 00751 m_from = from; 00752 m_graphValid = false; 00753 00754 // Initialize with "infinity" ... 00755 QAsciiDictIterator<Vertex> it( m_vertices ); 00756 for ( ; it.current(); ++it ) 00757 it.current()->reset(); 00758 00759 // ...and re-run the shortest path search for the new source mime 00760 shortestPaths(); 00761 } 00762 00763 KoFilterChain::Ptr Graph::chain( const KoFilterManager* manager, QCString& to ) const 00764 { 00765 if ( !isValid() || !manager ) 00766 return 0; 00767 00768 if ( to.isEmpty() ) { // if the destination is empty we search the closest KOffice part 00769 to = findKOfficePart(); 00770 if ( to.isEmpty() ) // still empty? strange stuff... 00771 return 0; 00772 } 00773 00774 const Vertex* vertex = m_vertices[ to ]; 00775 if ( !vertex || vertex->key() == UINT_MAX ) 00776 return 0; 00777 00778 KoFilterChain::Ptr ret = new KoFilterChain( manager ); 00779 00780 // Fill the filter chain with all filters on the path 00781 const Vertex* tmp = vertex->predecessor(); 00782 while ( tmp ) { 00783 const Edge* const edge = tmp->findEdge( vertex ); 00784 Q_ASSERT( edge ); 00785 ret->prependChainLink( edge->filterEntry(), tmp->mimeType(), vertex->mimeType() ); 00786 vertex = tmp; 00787 tmp = tmp->predecessor(); 00788 } 00789 return ret; 00790 } 00791 00792 void Graph::dump() const 00793 { 00794 kdDebug( 30500 ) << "+++++++++ Graph::dump +++++++++" << endl; 00795 kdDebug( 30500 ) << "From: " << m_from << endl; 00796 QAsciiDictIterator<Vertex> it( m_vertices ); 00797 for ( ; it.current(); ++it ) 00798 it.current()->dump( " " ); 00799 kdDebug( 30500 ) << "+++++++++ Graph::dump (done) +++++++++" << endl; 00800 } 00801 00802 // Query the trader and create the vertices and edges representing 00803 // available mime types and filters. 00804 void Graph::buildGraph() 00805 { 00806 // Make sure that all available parts are added to the graph 00807 QValueList<KoDocumentEntry> parts( KoDocumentEntry::query() ); 00808 QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() ); 00809 QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() ); 00810 00811 while ( partIt != partEnd ) { 00812 const QCString key( ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString().latin1() ); 00813 if ( !key.isEmpty() ) 00814 m_vertices.insert( key, new Vertex( key ) ); 00815 ++partIt; 00816 } 00817 00818 QValueList<KoFilterEntry::Ptr> filters( KoFilterEntry::query() ); // no constraint here - we want *all* :) 00819 QValueList<KoFilterEntry::Ptr>::ConstIterator it = filters.begin(); 00820 QValueList<KoFilterEntry::Ptr>::ConstIterator end = filters.end(); 00821 00822 for ( ; it != end; ++it ) { 00823 // First add the "starting points" to the dict 00824 QStringList::ConstIterator importIt = ( *it )->import.begin(); 00825 QStringList::ConstIterator importEnd = ( *it )->import.end(); 00826 for ( ; importIt != importEnd; ++importIt ) { 00827 const QCString key = ( *importIt ).latin1(); // latin1 is okay here (werner) 00828 // already there? 00829 if ( !m_vertices[ key ] ) 00830 m_vertices.insert( key, new Vertex( key ) ); 00831 } 00832 00833 // Are we allowed to use this filter at all? 00834 if ( KoFilterManager::filterAvailable( *it ) ) { 00835 QStringList::ConstIterator exportIt = ( *it )->export_.begin(); 00836 QStringList::ConstIterator exportEnd = ( *it )->export_.end(); 00837 00838 for ( ; exportIt != exportEnd; ++exportIt ) { 00839 // First make sure the export vertex is in place 00840 const QCString key = ( *exportIt ).latin1(); // latin1 is okay here 00841 Vertex* exp = m_vertices[ key ]; 00842 if ( !exp ) { 00843 exp = new Vertex( key ); 00844 m_vertices.insert( key, exp ); 00845 } 00846 // Then create the appropriate edges 00847 importIt = ( *it )->import.begin(); 00848 for ( ; importIt != importEnd; ++importIt ) 00849 m_vertices[ ( *importIt ).latin1() ]->addEdge( new Edge( exp, *it ) ); 00850 } 00851 } 00852 else 00853 kdDebug( 30500 ) << "Filter: " << ( *it )->service()->name() << " doesn't apply." << endl; 00854 } 00855 } 00856 00857 // As all edges (=filters) are required to have a positive weight 00858 // we can use Dijkstra's shortest path algorithm from Cormen's 00859 // "Introduction to Algorithms" (p. 527) 00860 // Note: I did some adaptions as our data structures are slightly 00861 // different from the ones used in the book. Further we simply stop 00862 // the algorithm is we don't find any node with a weight != Infinity 00863 // (==UINT_MAX), as this means that the remaining nodes in the queue 00864 // aren't connected anyway. 00865 void Graph::shortestPaths() 00866 { 00867 // Is the requested start mime type valid? 00868 Vertex* from = m_vertices[ m_from ]; 00869 if ( !from ) 00870 return; 00871 00872 // Inititalize start vertex 00873 from->setKey( 0 ); 00874 00875 // Fill the priority queue with all the vertices 00876 PriorityQueue<Vertex> queue( m_vertices ); 00877 00878 while ( !queue.isEmpty() ) { 00879 Vertex *min = queue.extractMinimum(); 00880 // Did we already relax all connected vertices? 00881 if ( min->key() == UINT_MAX ) 00882 break; 00883 min->relaxVertices( queue ); 00884 } 00885 m_graphValid = true; 00886 } 00887 00888 QCString Graph::findKOfficePart() const 00889 { 00890 // Here we simply try to find the closest KOffice mimetype 00891 QValueList<KoDocumentEntry> parts( KoDocumentEntry::query() ); 00892 QValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() ); 00893 QValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() ); 00894 00895 const Vertex *v = 0; 00896 00897 // Be sure that v gets initialized correctly 00898 while ( !v && partIt != partEnd ) { 00899 QString key( ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString() ); 00900 if ( !key.isEmpty() ) 00901 v = m_vertices[ key.latin1() ]; 00902 ++partIt; 00903 } 00904 if ( !v ) 00905 return ""; 00906 00907 // Now we try to find the "cheapest" KOffice vertex 00908 while ( partIt != partEnd ) { 00909 QString key( ( *partIt ).service()->property( "X-KDE-NativeMimeType" ).toString() ); 00910 Vertex* tmp = 0; 00911 if ( !key.isEmpty() ) 00912 tmp = m_vertices[ key.latin1() ]; 00913 00914 if ( tmp && tmp->key() < v->key() ) 00915 v = tmp; 00916 ++partIt; 00917 } 00918 00919 // It seems it already is a KOffice part 00920 if ( v->key() == 0 ) 00921 return ""; 00922 00923 return v->mimeType(); 00924 } 00925 00926 } // namespace KOffice
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Nov 17 06:54:15 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003