KoFilterChain.cpp
1/* This file is part of the KOffice libraries
2 Copyright (C) 2001 Werner Trobin <trobin@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
17*/
18
19#include <tqmetaobject.h>
20#include <tdetempfile.h>
21#include <kmimetype.h>
22#include <KoFilterChain.h>
23#include <KoQueryTrader.h>
24#include <KoFilterManager.h> // KoFilterManager::filterAvailable, private API
25#include <KoDocument.h>
26#include <kdebug.h>
27
28#include <priorityqueue.h>
29
30#include <limits.h> // UINT_MAX
31
32// Those "defines" are needed in the setupConnections method below.
33// Please always keep the strings and the length in sync!
34namespace {
35 const char* const SIGNAL_PREFIX = "commSignal";
36 const int SIGNAL_PREFIX_LEN = 10;
37 const char* const SLOT_PREFIX = "commSlot";
38 const int SLOT_PREFIX_LEN = 8;
39}
40
41
42KoFilterChain::ChainLink::ChainLink( KoFilterChain* chain, KoFilterEntry::Ptr filterEntry,
43 const TQCString& from, const TQCString& to ) :
44 m_chain( chain ), m_filterEntry( filterEntry ), m_from( from ), m_to( to ),
45 m_filter( 0 ), d( 0 )
46{
47}
48
49KoFilter::ConversionStatus KoFilterChain::ChainLink::invokeFilter( const ChainLink* const parentChainLink )
50{
51 if ( !m_filterEntry ) {
52 kdError( 30500 ) << "This filter entry is null. Strange stuff going on." << endl;
53 return KoFilter::CreationError;
54 }
55
56 m_filter = m_filterEntry->createFilter( m_chain, 0, 0 );
57
58 if ( !m_filter ) {
59 kdError( 30500 ) << "Couldn't create the filter." << endl;
60 return KoFilter::CreationError;
61 }
62
63 if ( parentChainLink )
64 setupCommunication( parentChainLink->m_filter );
65
66 KoFilter::ConversionStatus status = m_filter->convert( m_from, m_to );
67 delete m_filter;
68 m_filter=0;
69 return status;
70}
71
72void KoFilterChain::ChainLink::dump() const
73{
74 kdDebug( 30500 ) << " Link: " << m_filterEntry->service()->name() << endl;
75}
76
77int KoFilterChain::ChainLink::lruPartIndex() const
78{
79 if ( m_filter && m_filter->inherits( "KoEmbeddingFilter" ) )
80 return static_cast<KoEmbeddingFilter*>( m_filter )->lruPartIndex();
81 return -1;
82}
83
84void KoFilterChain::ChainLink::setupCommunication( const KoFilter* const parentFilter ) const
85{
86 // progress information
87 TQObject::connect( m_filter, TQ_SIGNAL( sigProgress( int ) ),
88 m_chain->manager(), TQ_SIGNAL( sigProgress( int ) ) );
89
90 if ( !parentFilter )
91 return;
92
93 const TQMetaObject* const parent = parentFilter->metaObject();
94 const TQMetaObject* const child = m_filter->metaObject();
95 if ( !parent || !child )
96 return;
97
98 setupConnections( parentFilter, parent->signalNames(), m_filter, child->slotNames() );
99 setupConnections( m_filter, child->signalNames(), parentFilter, parent->slotNames() );
100}
101
102void KoFilterChain::ChainLink::setupConnections( const KoFilter* sender, const TQStrList& sigs,
103 const KoFilter* receiver, const TQStrList& sl0ts ) const
104{
105 TQStrListIterator signalIt( sigs );
106 for ( ; signalIt.current(); ++signalIt ) {
107 if ( strncmp( signalIt.current(), SIGNAL_PREFIX, SIGNAL_PREFIX_LEN ) == 0 ) {
108 TQStrListIterator slotIt( sl0ts );
109 for ( ; slotIt.current(); ++slotIt ) {
110 if ( strncmp( slotIt.current(), SLOT_PREFIX, SLOT_PREFIX_LEN ) == 0 ) {
111 if ( strcmp( signalIt.current() + SIGNAL_PREFIX_LEN, slotIt.current() + SLOT_PREFIX_LEN ) == 0 ) {
112 TQCString signalString;
113 signalString.setNum( TQ_SIGNAL_CODE );
114 signalString += signalIt.current();
115 TQCString slotString;
116 slotString.setNum( TQ_SLOT_CODE );
117 slotString += slotIt.current();
118 TQObject::connect( sender, signalString, receiver, slotString );
119 }
120 }
121 }
122 }
123 }
124}
125
126
127KoFilterChain::~KoFilterChain()
128{
129 if ( filterManagerParentChain() && filterManagerParentChain()->m_outputStorage )
130 filterManagerParentChain()->m_outputStorage->leaveDirectory();
131 manageIO(); // Called for the 2nd time in a row -> clean up
132}
133
135{
136 KoFilter::ConversionStatus status = KoFilter::OK;
137
138 m_state = Beginning;
139 int count = m_chainLinks.count();
140
141 // This is needed due to nasty Microsoft design
142 const ChainLink* parentChainLink = 0;
143 if ( filterManagerParentChain() )
144 parentChainLink = filterManagerParentChain()->m_chainLinks.current();
145
146 // No iterator here, as we need m_chainLinks.current() in outputDocument()
147 m_chainLinks.first();
148 for ( ; count > 1 && m_chainLinks.current() && status == KoFilter::OK;
149 m_chainLinks.next(), --count ) {
150 status = m_chainLinks.current()->invokeFilter( parentChainLink );
151 m_state = Middle;
152 manageIO();
153 }
154
155 if ( !m_chainLinks.current() ) {
156 kdWarning( 30500 ) << "Huh?? Found a null pointer in the chain" << endl;
157 return KoFilter::StupidError;
158 }
159
160 if ( status == KoFilter::OK ) {
161 if ( m_state & Beginning )
162 m_state |= End;
163 else
164 m_state = End;
165 status = m_chainLinks.current()->invokeFilter( parentChainLink );
166 manageIO();
167 }
168
169 m_state = Done;
170 if (status == KoFilter::OK)
171 finalizeIO();
172 return status;
173}
174
176{
177 if ( m_state == Done )
178 return m_inputFile; // as we already called manageIO()
179 return TQString();
180}
181
183{
184 if ( m_inputQueried == File )
185 return m_inputFile;
186 else if ( m_inputQueried != Nil ) {
187 kdWarning( 30500 ) << "You already asked for some different source." << endl;
188 return TQString();
189 }
190 m_inputQueried = File;
191
192 if ( m_state & Beginning ) {
193 if ( static_cast<KoFilterManager::Direction>( filterManagerDirection() ) ==
194 KoFilterManager::Import )
195 m_inputFile = filterManagerImportFile();
196 else
197 inputFileHelper( filterManagerKoDocument(), filterManagerImportFile() );
198 }
199 else
200 if ( m_inputFile.isEmpty() )
201 inputFileHelper( m_inputDocument, TQString() );
202
203 return m_inputFile;
204}
205
207{
208 // sanity check: No embedded filter should ask for a plain file
209 // ###### CHECK: This will break as soon as we support exporting embedding filters
210 if ( filterManagerParentChain() )
211 kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl;
212
213 if ( m_outputQueried == File )
214 return m_outputFile;
215 else if ( m_outputQueried != Nil ) {
216 kdWarning( 30500 ) << "You already asked for some different destination." << endl;
217 return TQString();
218 }
219 m_outputQueried = File;
220
221 if ( m_state & End ) {
222 if ( static_cast<KoFilterManager::Direction>( filterManagerDirection() ) ==
223 KoFilterManager::Import )
224 outputFileHelper( false ); // This (last) one gets deleted by the caller
225 else
226 m_outputFile = filterManagerExportFile();
227 }
228 else
229 outputFileHelper( true );
230
231 return m_outputFile;
232}
233
234KoStoreDevice* KoFilterChain::storageFile( const TQString& name, KoStore::Mode mode )
235{
236 // ###### CHECK: This works only for import filters. Do we want something like
237 // that for export filters too?
238 if ( m_outputQueried == Nil && mode == KoStore::Write && filterManagerParentChain() )
239 return storageInitEmbedding( name );
240
241 // Plain normal use case
242 if ( m_inputQueried == Storage && mode == KoStore::Read &&
243 m_inputStorage && m_inputStorage->mode() == KoStore::Read )
244 return storageNewStreamHelper( &m_inputStorage, &m_inputStorageDevice, name );
245 else if ( m_outputQueried == Storage && mode == KoStore::Write &&
246 m_outputStorage && m_outputStorage->mode() == KoStore::Write )
247 return storageNewStreamHelper( &m_outputStorage, &m_outputStorageDevice, name );
248 else if ( m_inputQueried == Nil && mode == KoStore::Read )
249 return storageHelper( inputFile(), name, KoStore::Read,
250 &m_inputStorage, &m_inputStorageDevice );
251 else if ( m_outputQueried == Nil && mode == KoStore::Write )
252 return storageHelper( outputFile(), name, KoStore::Write,
253 &m_outputStorage, &m_outputStorageDevice );
254 else {
255 kdWarning( 30500 ) << "Oooops, how did we get here? You already asked for a"
256 << " different source/destination?" << endl;
257 return 0;
258 }
259}
260
262{
263 if ( m_inputQueried == Document )
264 return m_inputDocument;
265 else if ( m_inputQueried != Nil ) {
266 kdWarning( 30500 ) << "You already asked for some different source." << endl;
267 return 0;
268 }
269
270 if ( ( m_state & Beginning ) &&
271 static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Export &&
272 filterManagerKoDocument() )
273 m_inputDocument = filterManagerKoDocument();
274 else if ( !m_inputDocument )
275 m_inputDocument = createDocument( inputFile() );
276
277 m_inputQueried = Document;
278 return m_inputDocument;
279}
280
282{
283 // sanity check: No embedded filter should ask for a document
284 // ###### CHECK: This will break as soon as we support exporting embedding filters
285 if ( filterManagerParentChain() ) {
286 kdWarning( 30500 )<< "An embedded filter has to use storageFile()!" << endl;
287 return 0;
288 }
289
290 if ( m_outputQueried == Document )
291 return m_outputDocument;
292 else if ( m_outputQueried != Nil ) {
293 kdWarning( 30500 ) << "You already asked for some different destination." << endl;
294 return 0;
295 }
296
297 if ( ( m_state & End ) &&
298 static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Import &&
299 filterManagerKoDocument() )
300 m_outputDocument = filterManagerKoDocument();
301 else
302 m_outputDocument = createDocument( m_chainLinks.current()->to() );
303
304 m_outputQueried = Document;
305 return m_outputDocument;
306}
307
308void KoFilterChain::dump() const
309{
310 kdDebug( 30500 ) << "########## KoFilterChain with " << m_chainLinks.count() << " members:" << endl;
311 TQPtrListIterator<ChainLink> it( m_chainLinks );
312 for ( ; it.current(); ++it )
313 it.current()->dump();
314 kdDebug( 30500 ) << "########## KoFilterChain (done) ##########" << endl;
315}
316
317KoFilterChain::KoFilterChain( const KoFilterManager* manager ) :
318 m_manager( manager ), m_state( Beginning ), m_inputStorage( 0 ),
319 m_inputStorageDevice( 0 ), m_outputStorage( 0 ), m_outputStorageDevice( 0 ),
320 m_inputDocument( 0 ), m_outputDocument( 0 ), m_inputTempFile( 0 ),
321 m_outputTempFile( 0 ), m_inputQueried( Nil ), m_outputQueried( Nil ), d( 0 )
322{
323 // We "own" our chain links, the filter entries are implicitly shared
324 m_chainLinks.setAutoDelete( true );
325}
326
327void KoFilterChain::appendChainLink( KoFilterEntry::Ptr filterEntry, const TQCString& from, const TQCString& to )
328{
329 m_chainLinks.append( new ChainLink( this, filterEntry, from, to ) );
330}
331
332void KoFilterChain::prependChainLink( KoFilterEntry::Ptr filterEntry, const TQCString& from, const TQCString& to )
333{
334 m_chainLinks.prepend( new ChainLink( this, filterEntry, from, to ) );
335}
336
337void KoFilterChain::enterDirectory( const TQString& directory )
338{
339 // Only a little bit of checking as we (have to :} ) trust KoEmbeddingFilter
340 // If the output storage isn't initialized yet, we perform that step(s) on init.
341 if ( m_outputStorage )
342 m_outputStorage->enterDirectory( directory );
343 m_internalEmbeddingDirectories.append( directory );
344}
345
346void KoFilterChain::leaveDirectory()
347{
348 if ( m_outputStorage )
349 m_outputStorage->leaveDirectory();
350 if ( !m_internalEmbeddingDirectories.isEmpty() )
351 m_internalEmbeddingDirectories.pop_back();
352}
353
354TQString KoFilterChain::filterManagerImportFile() const
355{
356 return m_manager->importFile();
357}
358
359TQString KoFilterChain::filterManagerExportFile() const
360{
361 return m_manager->exportFile();
362}
363
364KoDocument* KoFilterChain::filterManagerKoDocument() const
365{
366 return m_manager->document();
367}
368
369int KoFilterChain::filterManagerDirection() const
370{
371 return m_manager->direction();
372}
373
374KoFilterChain* const KoFilterChain::filterManagerParentChain() const
375{
376 return m_manager->parentChain();
377}
378
379void KoFilterChain::manageIO()
380{
381 m_inputQueried = Nil;
382 m_outputQueried = Nil;
383
384 delete m_inputStorageDevice;
385 m_inputStorageDevice = 0;
386 if ( m_inputStorage ) {
387 m_inputStorage->close();
388 delete m_inputStorage;
389 m_inputStorage = 0;
390 }
391 if ( m_inputTempFile ) {
392 m_inputTempFile->close();
393 delete m_inputTempFile; // autodelete
394 m_inputTempFile = 0;
395 }
396 m_inputFile = TQString();
397
398 if ( !m_outputFile.isEmpty() ) {
399 m_inputFile = m_outputFile;
400 m_outputFile = TQString();
401 m_inputTempFile = m_outputTempFile;
402 m_outputTempFile = 0;
403
404 delete m_outputStorageDevice;
405 m_outputStorageDevice = 0;
406 if ( m_outputStorage ) {
407 m_outputStorage->close();
408 // Don't delete the storage if we're just pointing to the
409 // storage of the parent filter chain
410 if ( !filterManagerParentChain() || m_outputStorage->mode() != KoStore::Write )
411 delete m_outputStorage;
412 m_outputStorage = 0;
413 }
414 }
415
416 if ( m_inputDocument != filterManagerKoDocument() )
417 delete m_inputDocument;
418 m_inputDocument = m_outputDocument;
419 m_outputDocument = 0;
420}
421
422void KoFilterChain::finalizeIO()
423{
424 // In case we export (to a file, of course) and the last
425 // filter chose to output a KoDocument we have to save it.
426 // Should be very rare, but well...
427 // Note: m_*input*Document as we already called manageIO()
428 if ( m_inputDocument &&
429 static_cast<KoFilterManager::Direction>( filterManagerDirection() ) == KoFilterManager::Export ) {
430 kdDebug( 30500 ) << "Saving the output document to the export file" << endl;
431 m_inputDocument->saveNativeFormat( filterManagerExportFile() );
432 m_inputFile = filterManagerExportFile();
433 }
434}
435
436bool KoFilterChain::createTempFile( KTempFile** tempFile, bool autoDelete )
437{
438 if ( *tempFile ) {
439 kdError( 30500 ) << "Ooops, why is there already a temp file???" << endl;
440 return false;
441 }
442 *tempFile = new KTempFile();
443 ( *tempFile )->setAutoDelete( autoDelete );
444 return ( *tempFile )->status() == 0;
445}
446
447void KoFilterChain::inputFileHelper( KoDocument* document, const TQString& alternativeFile )
448{
449 if ( document ) {
450 if ( !createTempFile( &m_inputTempFile ) ) {
451 delete m_inputTempFile;
452 m_inputTempFile = 0;
453 m_inputFile = TQString();
454 return;
455 }
456 if ( !document->saveNativeFormat( m_inputTempFile->name() ) ) {
457 delete m_inputTempFile;
458 m_inputTempFile = 0;
459 m_inputFile = TQString();
460 return;
461 }
462 m_inputFile = m_inputTempFile->name();
463 }
464 else
465 m_inputFile = alternativeFile;
466}
467
468void KoFilterChain::outputFileHelper( bool autoDelete )
469{
470 if ( !createTempFile( &m_outputTempFile, autoDelete ) ) {
471 delete m_outputTempFile;
472 m_outputTempFile = 0;
473 m_outputFile = TQString();
474 }
475 else
476 m_outputFile = m_outputTempFile->name();
477}
478
479KoStoreDevice* KoFilterChain::storageNewStreamHelper( KoStore** storage, KoStoreDevice** device,
480 const TQString& name )
481{
482 delete *device;
483 *device = 0;
484 if ( ( *storage )->isOpen() )
485 ( *storage )->close();
486 if ( ( *storage )->bad() )
487 return storageCleanupHelper( storage );
488 if ( !( *storage )->open( name ) )
489 return 0;
490
491 *device = new KoStoreDevice( *storage );
492 return *device;
493}
494
495KoStoreDevice* KoFilterChain::storageHelper( const TQString& file, const TQString& streamName,
496 KoStore::Mode mode, KoStore** storage,
497 KoStoreDevice** device )
498{
499 if ( file.isEmpty() )
500 return 0;
501 if ( *storage ) {
502 kdDebug( 30500 ) << "Uh-oh, we forgot to clean up..." << endl;
503 return 0;
504 }
505
506 storageInit( file, mode, storage );
507
508 if ( ( *storage )->bad() )
509 return storageCleanupHelper( storage );
510
511 // Seems that we got a valid storage, at least. Even if we can't open
512 // the stream the "user" asked us to open, we nontheless change the
513 // IOState from File to Storage, as it might be possible to open other streams
514 if ( mode == KoStore::Read )
515 m_inputQueried = Storage;
516 else // KoStore::Write
517 m_outputQueried = Storage;
518
519 return storageCreateFirstStream( streamName, storage, device );
520}
521
522void KoFilterChain::storageInit( const TQString& file, KoStore::Mode mode, KoStore** storage )
523{
524 TQCString appIdentification( "" );
525 if ( mode == KoStore::Write ) {
526 // To create valid storages we also have to add the mimetype
527 // magic "applicationIndentifier" to the storage.
528 // As only filters with a KOffice destination should query
529 // for a storage to write to, we don't check the content of
530 // the mimetype here. It doesn't do a lot of harm if someome
531 // "abuses" this method.
532 appIdentification = m_chainLinks.current()->to();
533 }
534 *storage = KoStore::createStore( file, mode, appIdentification );
535}
536
537KoStoreDevice* KoFilterChain::storageInitEmbedding( const TQString& name )
538{
539 if ( m_outputStorage ) {
540 kdWarning( 30500 ) << "Ooops! Something's really screwed here." << endl;
541 return 0;
542 }
543
544 m_outputStorage = filterManagerParentChain()->m_outputStorage;
545
546 if ( !m_outputStorage ) {
547 // If the storage of the parent hasn't been initialized yet,
548 // we have to do that here. Quite nasty...
549 storageInit( filterManagerParentChain()->outputFile(), KoStore::Write, &m_outputStorage );
550
551 // transfer the ownership
552 filterManagerParentChain()->m_outputStorage = m_outputStorage;
553 filterManagerParentChain()->m_outputQueried = Storage;
554 }
555
556 if ( m_outputStorage->isOpen() )
557 m_outputStorage->close(); // to be on the safe side, should never happen
558 if ( m_outputStorage->bad() )
559 return storageCleanupHelper( &m_outputStorage );
560
561 m_outputQueried = Storage;
562
563 // Now that we have a storage we have to change the directory
564 // and remember it for later!
565 const int lruPartIndex = filterManagerParentChain()->m_chainLinks.current()->lruPartIndex();
566 if ( lruPartIndex == -1 ) {
567 kdError( 30500 ) << "Huh! You want to use embedding features w/o inheriting KoEmbeddingFilter?" << endl;
568 return storageCleanupHelper( &m_outputStorage );
569 }
570
571 if ( !m_outputStorage->enterDirectory( TQString( "part%1" ).arg( lruPartIndex ) ) )
572 return storageCleanupHelper( &m_outputStorage );
573
574 return storageCreateFirstStream( name, &m_outputStorage, &m_outputStorageDevice );
575}
576
577KoStoreDevice* KoFilterChain::storageCreateFirstStream( const TQString& streamName, KoStore** storage,
578 KoStoreDevice** device )
579{
580 // Before we go and create the first stream in this storage we
581 // have to perform a little hack in case we're used by any ole-style
582 // filter which utilizes internal embedding. Ugly, but well...
583 if ( !m_internalEmbeddingDirectories.isEmpty() ) {
584 TQStringList::ConstIterator it = m_internalEmbeddingDirectories.begin();
585 TQStringList::ConstIterator end = m_internalEmbeddingDirectories.end();
586 for ( ; it != end && ( *storage )->enterDirectory( *it ); ++it );
587 }
588
589 if ( !( *storage )->open( streamName ) )
590 return 0;
591
592 if ( *device ) {
593 kdDebug( 30500 ) << "Uh-oh, we forgot to clean up the storage device!" << endl;
594 ( *storage )->close();
595 return storageCleanupHelper( storage );
596 }
597 *device = new KoStoreDevice( *storage );
598 return *device;
599}
600
601KoStoreDevice* KoFilterChain::storageCleanupHelper( KoStore** storage )
602{
603 // Take care not to delete the storage of the parent chain
604 if ( *storage != m_outputStorage || !filterManagerParentChain() ||
605 ( *storage )->mode() != KoStore::Write )
606 delete *storage;
607 *storage = 0;
608 return 0;
609}
610
611KoDocument* KoFilterChain::createDocument( const TQString& file )
612{
613 KURL url;
614 url.setPath( file );
615 KMimeType::Ptr t = KMimeType::findByURL( url, 0, true );
616 if ( t->name() == KMimeType::defaultMimeType() ) {
617 kdError( 30500 ) << "No mimetype found for " << file << endl;
618 return 0;
619 }
620
621 KoDocument *doc = createDocument( TQCString( t->name().latin1() ) );
622
623 if ( !doc || !doc->loadNativeFormat( file ) ) {
624 kdError( 30500 ) << "Couldn't load from the file" << endl;
625 delete doc;
626 return 0;
627 }
628 return doc;
629}
630
631KoDocument* KoFilterChain::createDocument( const TQCString& mimeType )
632{
634
635 if (entry.isEmpty())
636 {
637 kdError( 30500 ) << "Couldn't find a part that can handle mimetype " << mimeType << endl;
638 }
639
640 KoDocument* doc = entry.createDoc(); /*entries.first().createDoc();*/
641 if ( !doc ) {
642 kdError( 30500 ) << "Couldn't create the document" << endl;
643 return 0;
644 }
645 return doc;
646}
647
648
649namespace KOffice {
650
651 Edge::Edge( Vertex* vertex, KoFilterEntry::Ptr filterEntry ) :
652 m_vertex( vertex ), m_filterEntry( filterEntry ), d( 0 )
653 {
654 }
655
656 void Edge::relax( const Vertex* predecessor, PriorityQueue<Vertex>& queue )
657 {
658 if ( !m_vertex || !predecessor || !m_filterEntry )
659 return;
660 if ( m_vertex->setKey( predecessor->key() + m_filterEntry->weight ) ) {
661 queue.keyDecreased( m_vertex ); // maintain the heap property
662 m_vertex->setPredecessor( predecessor );
663 }
664 }
665
666 void Edge::dump( const TQCString& indent ) const
667 {
668 if ( m_vertex )
669 kdDebug( 30500 ) << indent << "Edge -> '" << m_vertex->mimeType()
670 << "' (" << m_filterEntry->weight << ")" << endl;
671 else
672 kdDebug( 30500 ) << indent << "Edge -> '(null)' ("
673 << m_filterEntry->weight << ")" << endl;
674 }
675
676
677 Vertex::Vertex( const TQCString& mimeType ) : m_predecessor( 0 ), m_mimeType( mimeType ),
678 m_weight( UINT_MAX ), m_index( -1 ), d( 0 )
679 {
680 m_edges.setAutoDelete( true ); // we take ownership of added edges
681 }
682
683 bool Vertex::setKey( unsigned int key )
684 {
685 if ( m_weight > key ) {
686 m_weight = key;
687 return true;
688 }
689 return false;
690 }
691
692 void Vertex::reset()
693 {
694 m_weight = UINT_MAX;
695 m_predecessor = 0;
696 }
697
698 void Vertex::addEdge( const Edge* edge )
699 {
700 if ( !edge || edge->weight() == 0 )
701 return;
702 m_edges.append( edge );
703 }
704
705 const Edge* Vertex::findEdge( const Vertex* vertex ) const
706 {
707 if ( !vertex )
708 return 0;
709 const Edge* edge = 0;
710 TQPtrListIterator<Edge> it( m_edges );
711
712 for ( ; it.current(); ++it ) {
713 if ( it.current()->vertex() == vertex &&
714 ( !edge || it.current()->weight() < edge->weight() ) )
715 edge = it.current();
716 }
717 return edge;
718 }
719
720 void Vertex::relaxVertices( PriorityQueue<Vertex>& queue )
721 {
722 for ( Edge *e = m_edges.first(); e; e = m_edges.next() )
723 e->relax( this, queue );
724 }
725
726 void Vertex::dump( const TQCString& indent ) const
727 {
728 kdDebug( 30500 ) << indent << "Vertex: " << m_mimeType << " (" << m_weight << "):" << endl;
729 const TQCString i( indent + " " );
730 TQPtrListIterator<Edge> it( m_edges );
731 for ( ; it.current(); ++it )
732 it.current()->dump( i );
733 }
734
735
736 Graph::Graph( const TQCString& from ) : m_vertices( 47 ), m_from( from ),
737 m_graphValid( false ), d( 0 )
738 {
739 m_vertices.setAutoDelete( true );
740 buildGraph();
741 shortestPaths(); // Will return after a single lookup if "from" is invalid (->no check here)
742 }
743
744 void Graph::setSourceMimeType( const TQCString& from )
745 {
746 if ( from == m_from )
747 return;
748 m_from = from;
749 m_graphValid = false;
750
751 // Initialize with "infinity" ...
752 TQAsciiDictIterator<Vertex> it( m_vertices );
753 for ( ; it.current(); ++it )
754 it.current()->reset();
755
756 // ...and re-run the shortest path search for the new source mime
757 shortestPaths();
758 }
759
760 KoFilterChain::Ptr Graph::chain( const KoFilterManager* manager, TQCString& to ) const
761 {
762 if ( !isValid() || !manager )
763 return 0;
764
765 if ( to.isEmpty() ) { // if the destination is empty we search the closest KOffice part
766 to = findKOfficePart();
767 if ( to.isEmpty() ) // still empty? strange stuff...
768 return 0;
769 }
770
771 const Vertex* vertex = m_vertices[ to ];
772 if ( !vertex || vertex->key() == UINT_MAX )
773 return 0;
774
775 KoFilterChain::Ptr ret = new KoFilterChain( manager );
776
777 // Fill the filter chain with all filters on the path
778 const Vertex* tmp = vertex->predecessor();
779 while ( tmp ) {
780 const Edge* const edge = tmp->findEdge( vertex );
781 Q_ASSERT( edge );
782 ret->prependChainLink( edge->filterEntry(), tmp->mimeType(), vertex->mimeType() );
783 vertex = tmp;
784 tmp = tmp->predecessor();
785 }
786 return ret;
787 }
788
789 void Graph::dump() const
790 {
791 kdDebug( 30500 ) << "+++++++++ Graph::dump +++++++++" << endl;
792 kdDebug( 30500 ) << "From: " << m_from << endl;
793 TQAsciiDictIterator<Vertex> it( m_vertices );
794 for ( ; it.current(); ++it )
795 it.current()->dump( " " );
796 kdDebug( 30500 ) << "+++++++++ Graph::dump (done) +++++++++" << endl;
797 }
798
799 // Query the trader and create the vertices and edges representing
800 // available mime types and filters.
801 void Graph::buildGraph()
802 {
803 // Make sure that all available parts are added to the graph
804 TQValueList<KoDocumentEntry> parts( KoDocumentEntry::query() );
805 TQValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
806 TQValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
807
808 while ( partIt != partEnd ) {
809 TQStringList nativeMimeTypes = ( *partIt ).service()->property( "X-TDE-ExtraNativeMimeTypes" ).toStringList();
810 nativeMimeTypes += ( *partIt ).service()->property( "X-TDE-NativeMimeType" ).toString();
811 TQStringList::ConstIterator it = nativeMimeTypes.begin();
812 TQStringList::ConstIterator end = nativeMimeTypes.end();
813 for ( ; it != end; ++it )
814 if ( !(*it).isEmpty() )
815 m_vertices.insert( (*it).latin1(), new Vertex( (*it).latin1() ) );
816 ++partIt;
817 }
818
819 TQValueList<KoFilterEntry::Ptr> filters( KoFilterEntry::query() ); // no constraint here - we want *all* :)
820 TQValueList<KoFilterEntry::Ptr>::ConstIterator it = filters.begin();
821 TQValueList<KoFilterEntry::Ptr>::ConstIterator end = filters.end();
822
823 for ( ; it != end; ++it ) {
824 // First add the "starting points" to the dict
825 TQStringList::ConstIterator importIt = ( *it )->import.begin();
826 TQStringList::ConstIterator importEnd = ( *it )->import.end();
827 for ( ; importIt != importEnd; ++importIt ) {
828 const TQCString key = ( *importIt ).latin1(); // latin1 is okay here (werner)
829 // already there?
830 if ( !m_vertices[ key ] )
831 m_vertices.insert( key, new Vertex( key ) );
832 }
833
834 // Are we allowed to use this filter at all?
836 TQStringList::ConstIterator exportIt = ( *it )->export_.begin();
837 TQStringList::ConstIterator exportEnd = ( *it )->export_.end();
838
839 for ( ; exportIt != exportEnd; ++exportIt ) {
840 // First make sure the export vertex is in place
841 const TQCString key = ( *exportIt ).latin1(); // latin1 is okay here
842 Vertex* exp = m_vertices[ key ];
843 if ( !exp ) {
844 exp = new Vertex( key );
845 m_vertices.insert( key, exp );
846 }
847 // Then create the appropriate edges
848 importIt = ( *it )->import.begin();
849 for ( ; importIt != importEnd; ++importIt )
850 m_vertices[ ( *importIt ).latin1() ]->addEdge( new Edge( exp, *it ) );
851 }
852 }
853 else
854 kdDebug( 30500 ) << "Filter: " << ( *it )->service()->name() << " doesn't apply." << endl;
855 }
856 }
857
858 // As all edges (=filters) are required to have a positive weight
859 // we can use Dijkstra's shortest path algorithm from Cormen's
860 // "Introduction to Algorithms" (p. 527)
861 // Note: I did some adaptions as our data structures are slightly
862 // different from the ones used in the book. Further we simply stop
863 // the algorithm is we don't find any node with a weight != Infinity
864 // (==UINT_MAX), as this means that the remaining nodes in the queue
865 // aren't connected anyway.
866 void Graph::shortestPaths()
867 {
868 // Is the requested start mime type valid?
869 Vertex* from = m_vertices[ m_from ];
870 if ( !from )
871 return;
872
873 // Inititalize start vertex
874 from->setKey( 0 );
875
876 // Fill the priority queue with all the vertices
877 PriorityQueue<Vertex> queue( m_vertices );
878
879 while ( !queue.isEmpty() ) {
880 Vertex *min = queue.extractMinimum();
881 // Did we already relax all connected vertices?
882 if ( min->key() == UINT_MAX )
883 break;
884 min->relaxVertices( queue );
885 }
886 m_graphValid = true;
887 }
888
889 TQCString Graph::findKOfficePart() const
890 {
891 // Here we simply try to find the closest KOffice mimetype
892 TQValueList<KoDocumentEntry> parts( KoDocumentEntry::query() );
893 TQValueList<KoDocumentEntry>::ConstIterator partIt( parts.begin() );
894 TQValueList<KoDocumentEntry>::ConstIterator partEnd( parts.end() );
895
896 const Vertex *v = 0;
897
898 // Be sure that v gets initialized correctly
899 while ( !v && partIt != partEnd ) {
900 TQStringList nativeMimeTypes = ( *partIt ).service()->property( "X-TDE-ExtraNativeMimeTypes" ).toStringList();
901 nativeMimeTypes += ( *partIt ).service()->property( "X-TDE-NativeMimeType" ).toString();
902 TQStringList::ConstIterator it = nativeMimeTypes.begin();
903 TQStringList::ConstIterator end = nativeMimeTypes.end();
904 for ( ; !v && it != end; ++it )
905 if ( !(*it).isEmpty() )
906 v = m_vertices[ ( *it ).latin1() ];
907 ++partIt;
908 }
909 if ( !v )
910 return "";
911
912 // Now we try to find the "cheapest" KOffice vertex
913 while ( partIt != partEnd ) {
914 TQStringList nativeMimeTypes = ( *partIt ).service()->property( "X-TDE-ExtraNativeMimeTypes" ).toStringList();
915 nativeMimeTypes += ( *partIt ).service()->property( "X-TDE-NativeMimeType" ).toString();
916 TQStringList::ConstIterator it = nativeMimeTypes.begin();
917 TQStringList::ConstIterator end = nativeMimeTypes.end();
918 for ( ; !v && it != end; ++it ) {
919 TQString key = *it;
920 if ( !key.isEmpty() ) {
921 Vertex* tmp = m_vertices[ key.latin1() ];
922 if ( !v || ( tmp && tmp->key() < v->key() ) )
923 v = tmp;
924 }
925 }
926 ++partIt;
927 }
928
929 // It seems it already is a KOffice part
930 if ( v->key() == 0 )
931 return "";
932
933 return v->mimeType();
934 }
935
936} // namespace KOffice
A document that can contain a lot of formulas (container).
Represents an available KOffice component that supports the document interface.
static KoDocumentEntry queryByMimeType(const TQString &mimetype)
This is a convenience function.
bool isEmpty() const
static TQValueList< KoDocumentEntry > query(const TQString &_constr=TQString())
This function will query tdesycoca to find all available components.
KoDocument * createDoc(KoDocument *parent=0, const char *name=0) const
Uses the factory of the component to create a document.
The KOffice document class.
Definition KoDocument.h:61
virtual bool loadNativeFormat(const TQString &file)
Loads a document in the native format from a given URL.
virtual bool saveNativeFormat(const TQString &file)
Saves the document in native format, to a given file You should never have to reimplement.
The base class for all import filters embedding other filters.
Definition KoFilter.h:148
This class represents a chain of plain KoFilter instances.
TQString inputFile()
Get the current file to read from.
KoFilter::ConversionStatus invokeChain()
Starts the filtering process.
TQString outputFile()
Get the current file to write to.
KoStoreDevice * storageFile(const TQString &name="root", KoStore::Mode mode=KoStore::Read)
Get a file from a storage.
KoDocument * inputDocument()
This method allows your filter to work directly on the KoDocument of the application.
TQString chainOutput() const
Tells the KoFilterManager the output file of the filter chain in case of an import operation.
KoDocument * outputDocument()
This method allows your filter to work directly on the KoDocument of the application.
static TQValueList< KoFilterEntry::Ptr > query(const TQString &_constr=TQString())
This function will query KDED to find all available filters.
The class managing all the filters.
Direction
This enum is used to distinguish the import/export cases.
static bool filterAvailable(KoFilterEntry::Ptr entry)
Method used to check if that filter is available at all.
The base class for import and export filters.
Definition KoFilter.h:50
ConversionStatus
This enum is used to signal the return state of your filter.
Definition KoFilter.h:64
This class implements a TQIODevice around KoStore, so that it can be used to create a TQDomDocument f...
Saves and loads KOffice documents using various backends.
Definition KoStore.h:40
bool close()
Close the file inside the store.
Definition KoStore.cpp:242
bool isOpen() const
Check whether a file inside the store is currently opened with open(), ready to be read or written.
Definition KoStore.cpp:237
bool leaveDirectory()
Leaves a directory.
Definition KoStore.cpp:390
bool bad() const
Definition KoStore.h:158
static KoStore * createStore(const TQString &fileName, Mode mode, const TQCString &appIdentification="", Backend backend=Auto)
Open a store (i.e.
Definition KoStore.cpp:58
bool enterDirectory(const TQString &directory)
Enters one or multiple directories.
Definition KoStore.cpp:374
Mode mode() const
Definition KoStore.h:163
Namespace for general KOFFICE functions.